New Upstream Snapshot - mergerfs

Ready changes

Summary

Merged new upstream version: 2.34.1+git20230116.1.2d8d0f7 (was: 2.33.5+git20221111.1.629806e).

Resulting package

Built on 2023-01-20T09:54 (took 15m29s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots mergerfs-dbgsymapt install -t fresh-snapshots mergerfs

Lintian Result

Diff

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index fe22899..0000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-# These are supported funding model platforms
-
-github: [trapexit]
-patreon: trapexit
-custom: ['https://paypal.me/trapexit','https://buymeacoff.ee/trapexit']
-ko_fi: trapexit
-open_collective: trapexit
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 07082f8..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,47 +0,0 @@
----
-name: Bug report
-about: For the reporting of behavior not as described
-title: ''
-labels: bug, investigating
-assignees: ''
-
----
-
-**Describe the bug**
-
-A clear and concise description of the unexpected behavior. Please be sure to use latest release of mergerfs to ensure the issue still exists.
-
-The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
-
-
-**To Reproduce**
-
-Steps to reproduce the behavior. List **all** steps to reproduce. **All** settings.
-
-Please simplify the reproduction as much as possible. 
- - Unless it is dependenat on multiple branches, use a single branch
- - Use standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.)
-
-
-**Expected behavior**
-
-A clear and concise description of the expected behavior.
-
-
-**System information:**
-
- - OS, kernel version: `uname -a`
- - mergerfs version: `mergerfs -V`
- - mergerfs settings
- - List of drives, filesystems, & sizes:
-   - `df -h`
-   - `lsblk -f`
- - A strace of the application having a problem: 
-   - `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
-   - `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
- - strace of mergerfs while app tried to do it's thing:
-   - `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
-
-**Additional context**
-
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index cd230da..0000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: feature, investigating
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is.
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
deleted file mode 100644
index a82a5d7..0000000
--- a/.github/ISSUE_TEMPLATE/question.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-name: Question
-about: When you're looking for information
-title: ''
-labels: investigating, question
-assignees: ''
-
----
-
-You can also ask questions on Discord for realtime help: https://discord.gg/MpAr69V
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index aba2dc0..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,895 +0,0 @@
-mergerfs (2.33.5~debian-buster) buster; urgency=medium
-
-  * 14c2ff9 Return ENOENT when dotdot for root node requested
- -- trapexit <trapexit@spawn.link>  Tue, 12 Apr 2022 23:18:57 -0400
-
-mergerfs (2.33.4~debian-buster) buster; urgency=medium
-
-  * b95ff8b Lock less often during logging
-  * 3f060f4 Change ENOENT to ESTALE for looking up paths to handle rename race conditions
-  * c9a9358 Fix query of attr during symlink
-  * 9ca10b2 Rework node slab garbage collection to limit blocking work threads
-  * e3ab739 Update bug_report.md
-  * 58803f4 Update README.md
- -- trapexit <trapexit@spawn.link>  Sun, 20 Mar 2022 22:45:07 -0400
-
-mergerfs (2.33.3~debian-buster) buster; urgency=medium
-
-  * c43b464 Call FUSE getattr rather than syscall for extra logic
- -- trapexit <trapexit@spawn.link>  Sat, 18 Dec 2021 17:21:19 -0500
-
-mergerfs (2.33.2~debian-buster) buster; urgency=medium
-
-  * e256c88 Get attrs for link, not target
- -- trapexit <trapexit@spawn.link>  Thu, 16 Dec 2021 19:35:18 -0500
-
-mergerfs (2.33.1~debian-buster) buster; urgency=medium
-
-  * ee8532c Ensure server handling of locks is disabled
- -- trapexit <trapexit@spawn.link>  Sat, 27 Nov 2021 00:02:55 -0500
-
-mergerfs (2.33.0~debian-buster) buster; urgency=medium
-
-  * 68b3026 Remove embedded name in node struct
-  * 18dead4 Add new debug printing routines
-  * 68719da Remove ioctl runtime section, use getfattr instead of xattr
-  * c384509 Remove subscribestar from sponsor platforms
-  * 5f737cb Add option to log node memory usage metrics
-  * 930dad3 Reduce struct node size
-  * 8150957 Fix regression from remember_node refactor
-  * c929781 Add malloc_trim configure test
-  * 313aa2a Update ghc::filesystem and nonstd::optional
-  * 4ea0de3 Rework dirents buffer management
-  * 6b5c484 Major rework of memory allocation using fixed mem pools
-  * f598d3b Add details on dropping caches before benchmarks
-  * 122f9c1 Add new cirrus-ci targets
-  * 5263a65 Remove usage printing regression in mount_bsd
-  * 8def16a Fix infinite loop in mfs action policy
-  * 43a6d66 Major cleanup of libfuse to remove unneeded features
-  * df721eb Update docs with Chia wallet address
-  * 54e8500 Add more clarity in what to provide when submitting bug reports or questions
-  * 1aaf742 Update 'features' section of readme
-  * b4c031d tweak bug report template language
-  * 61c2187 Remove config ioctl calls
-  * 7013ff9 Update nonstd::optional to v3.4.0
-  * 3ce0dc5 Update ghc::filesystem to v1.5.8
-  * 098f353 pfrd: fix mod by zero error when all branches are filtered
-  * c06db9c pfrd: fix mod by zero error when all branches are filtered
-  * 80f3099 properly initialize fuse_buf in worker loop
-  * bcd5fde properly initialize fuse_buf in worker loop
-  * c40f42e mention bind-propagation and add link to hotio
-  * 44efbaa Update README.md
-  * 9573a05 Update bug_report.md
-  * 495afef README: update strace args and add question on snapraid
-  * 58b4eb9 Fix typo
-  * 9a90609 remove travis-ci
-  * 8adebc9 new features: follow-symlinks, rename-exdev, link-exdev
-  * ed0c1db fix parsing of relative branch paths
-  * d337574 fix parsing of relative branch paths
-  * 6a9f7e6 readme: update support section
-  * 3bc189c README: update support section
-  * dfb544f README: add warning/clearity about caching script and hardlinks and cow
-  * 538467b config: rework global config, remove rwlock, make branches RCU like
-  * 3808e26 Update FUNDING.yml
-  * e9e17ba README: misc updates and tweaks
- -- trapexit <trapexit@spawn.link>  Mon, 1 Nov 2021 22:46:09 -0400
-
-mergerfs (2.32.6~debian-buster) buster; urgency=medium
-
-  * c06db9c pfrd: fix mod by zero error when all branches are filtered
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 25 Jun 2021 09:15:44 -0400
-
-mergerfs (2.32.5~debian-buster) buster; urgency=medium
-
-  * bcd5fde properly initialize fuse_buf in worker loop
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 20 Jun 2021 17:11:58 -0400
-
-mergerfs (2.32.4~debian-buster) buster; urgency=medium
-
-  * ed0c1db fix parsing of relative branch paths
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 11 Feb 2021 19:49:50 -0500
-
-mergerfs (2.32.3~debian-buster) buster; urgency=medium
-
-  * f6e3767 reset dentry buffer when rewind'ed
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 9 Feb 2021 18:09:28 -0500
-
-mergerfs (2.32.2~debian-buster) buster; urgency=medium
-
-  * f50812a dirname should not return an empty string
-  * 5e08357 update cirrus-ci builds
-  * 44a9891 msp policies stopped before root path
- -- trapexit <trapexit@spawn.link>  Fri, 18 Dec 2020 17:03:43 -0500
-
-mergerfs (2.32.1~debian-buster) buster; urgency=medium
-
-  * 3900543 fix segv: zero out data structures
- -- trapexit <trapexit@spawn.link>  Sun, 13 Dec 2020 16:56:55 -0500
-
-mergerfs (2.32.0~debian-buster) buster; urgency=medium
-
-  * 7edd3c6 config: fix invalid error check when parsing config file
-  * 1b26f49 general cleanup, slight memory reduction
-  * c3fffef msp policies: used wrong path to check existance
-  * 7e583e3 use relative option for rsync
- -- trapexit <trapexit@spawn.link>  Sat, 28 Nov 2020 21:49:51 -0500
-
-mergerfs (2.31.0~debian-buster) buster; urgency=medium
-
-  * 8990e24 README.md: add details on per branch minfreespace
-  * f6e37c5 README.md: add details about error handling
-  * 0484442 wyhash: use safety mode 1
-  * 5a31843 rework some function error handling
-  * 610c75f properly return const ref from tofrom string wrapper
-  * 73e8ed7 Delete issue_template.md
-  * 0b1af2b Update bug_report.md
-  * 767039c option_parser: return 0 when requesting help or version
-  * fc34539 branches: add per branch minfreespace w/ original value as default
-  * 27bd39f README.md: fix typos
-  * 6311df7 optionally use lchmod depending on if on Linux or not (BSD)
-  * 15fb751 README.md: add note indicating only tagged releases are supported
-  * 0468440 add {,ep,msp}pfrd policies
-  * 8675fce add fedora builds to cirrus-ci
-  * 7fd629e add #warning to make it more obvious what versions of functions are used
-  * 2fe20b8 clean up and separate out fs_* files
-  * 2696079 break fs.hpp up into separate files
-  * ec15872 cleanup function signatures and definitions
-  * 6cc6524 change category to enum class
-  * 7e17310 libfuse cleanup: add more header include guards
-  * f488deb libfuse/Makefile: support CXXFLAGS and LDFLAGS passed via environment
-  * 678626e Makefile: support CXXFLAGS and LDFLAGS passed via environment
-  * 0bc6711 libfuse/Makefile: support $AR
-  * a925fbe libfuse cleanup: extern cplusplus cleanup
-  * dc1b698 libfuse cleanup: remove single threaded
-  * 3c761b7 libfuse cleanup: remove libfuse API compatibility
-  * 3bfdd78 libfuse cleanup: remove cuse
-  * 5f12fb6 libfuse cleanup: remove unnecessary files
-  * f9b831e libfuse cleanup: reindent
-  * badf7e1 README.md: add XMR address
-  * 3b38262 README.md: update support section with Open Collective and LBC
-  * 0371b04 change from fasthash64 to wyhash
-  * 1fe32e9 fix: add fakeroot needed for building
- -- trapexit <trapexit@spawn.link>  Tue, 29 Sep 2020 18:51:30 -0400
-
-mergerfs (2.30.0~debian-buster) buster; urgency=medium
-
-  * 30d13b7 inodecalc: add 32bit versions of hashs
-  * 5989d41 readdir: use getdents64 for compatibility with ARM64
-  * 1d2a1ba README.md: move kernel bugs to wiki
-  * e639f3d README.md: update faq regarding Plex and page caching
-  * 173193d Update issue templates
-  * 0709b2e add missing options to usage
-  * 15a0aed cleanup: move some config data structures to separate files
-  * 8afe72d README: change back to listing fuse.mergerfs for fstab fstype
-  * a93bd9f freebsd: misc cleanups to get freebsd compiling
-  * 139e61e nfsopenhack: remove empty file check
-  * 93218a3 NFS open/creat hack
-  * 3ec137c policy: add "most shared path" policies
-  * 3a67384 Fix typos
-  * 468d420 Removed duplicated include
-  * 08d267f moveonenospc: enhance the feature to allow using a policy
-  * c4a85f5 readdir: add dirent index array
-  * c099064 remove unnecessary libfuse flags nopath, nullpath_ok, and utime_omit_ok
-  * 6289956 temporary fix for short readdirs on NFS
-  * d699a97 fix rpm spec and chmod in deb
-  * 8ed3a1f debian: fix installing of fresh deb
-  * b4397f7 add 'inodecalc' option to allow selection of inode calculation algo
-  * dbdd3e2 additional readdir refactor cleanup
-  * afb07b1 add Cirrus-CI
-  * 54c41c4 rework config management
-  * a0c1c1a Travis-CI: add installing of deb package
-  * add588f README: add terminology section and tweak some things
-  * eb78c51 README: update support section
-  * c6bce81 fix install location and setuid setting in deb pkgs
-  * aad2257 README.md: add human readable versions of some errno references
-  * eff15c7 README.md: request users update to latest version before they submit report
-  * 7c37a69 add basic setup section
-  * 698c414 fix getdent name length calculation
-  * 0888ee1 README: update a number of sections and fix typos
-  * 8ba1aba README: add note regarding space calculations
-  * 3a46ec9 fix filename hashing error
-  * 9b2634a fix name length calculation for musc
-  * 62873d2 use getdents64 on linux
-  * d119807 restructure readdir, add readdir_plus
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 3 Aug 2020 19:42:06 -0400
-
-mergerfs (2.29.0~debian-buster) buster; urgency=medium
-
-  * 5ce428c rework makefiles + install mount tools
-  * a646fe0 change inode conversion algo to reduce collision
-  * 576ff36 add cache.writeback to xattrs
-  * bf4b390 update fuse_kernel.h
-  * 4b9f3de add ctime support
-  * 903d39f add writeback caching
-  * 9952c58 add links to ZFS comparison
-  * 66f0d9e add faq entry on files ending up on 1 branch
-  * 0f2f78c python 2.6 fix
-  * 489ab23 make git2debcl work on Python 2 & 3
-  * 3897852 add README segment on benchmarking
-  * f4b8efc more FAQs
-  * 4625a3c fix typos and update FAQ regarding policy preference
-  * 322fa57 update docs: openvz kernel bug and new support links
-  * 5eb3c8c add github sponsor and subscribestar to funding
-  * 5d82756 Create FUNDING.yml
- -- trapexit <trapexit@spawn.link>  Sat, 22 Feb 2020 17:00:55 -0500
-
-mergerfs (2.28.3~debian-buster) buster; urgency=medium
-
-  * 4d82ed9 fix short writes on >2GB files when cloning file
-  * 08e1bef improve nodeid generation
-  * 58ab7f7 Fix typo
-  * 7a84555 update travis config to include bionic
- -- trapexit <trapexit@spawn.link>  Mon, 14 Oct 2019 23:34:36 -0400
-
-mergerfs (2.28.2~debian-buster) buster; urgency=medium
-
-  * 0fffabf only return 1 branch for rand/eprand policies
-  * 50ad648 initialize mutex to fix lockup
-  * 752a159 add UPGRADE section
-  * f77d1d9 Force symlink creation (don't error if it already exists)
-  * e6a6ab9 update how it works
-  * 38919ad Update README.md
-  * 86bda61 fix some grammar and typos
-  * 4c4c27a set uid & gid when calling ioctl
- -- trapexit <trapexit@spawn.link>  Sat, 7 Sep 2019 17:27:56 -0400
-
-mergerfs (2.28.1~debian-buster) buster; urgency=medium
-
-  * 5ca928e accept old arguments for backwards compatibility
- -- trapexit <trapexit@spawn.link>  Mon, 10 Jun 2019 20:24:36 -0400
-
-mergerfs (2.28.0~debian-buster) buster; urgency=medium
-
-  * 7cbd88a allow setting of 'max_pages' (via 'fuse_msg_size')
-  * 8cb7195 add copy_file_range support
-  * 529a953 add file caching across opens and runtime control
-  * ddf6a2f make async_read optional again
-  * 2323c16 add readdir caching
-  * 1baa706 add symlink caching
- -- trapexit <trapexit@spawn.link>  Mon, 3 Jun 2019 21:16:53 -0400
-
-mergerfs (2.27.1~debian-buster) buster; urgency=medium
-
-  * 61cded5 fix for unlink race condition
- -- trapexit <trapexit@spawn.link>  Tue, 21 May 2019 23:09:13 -0400
-
-mergerfs (2.27.0~debian-buster) buster; urgency=medium
-
-  * 229d851 fix mount.mergerfs generation
-  * 5f22211 ensure parallel dirops is enabled if capable
-  * 2b019b8 ensure async_aio is enabled if capable
-  * 3a66a68 ensure marking open files renamed over as hidden
-  * 80d56ac add support for POSIX ACLs
-  * 1ca7052 remove 'remote' flock support
- -- trapexit <trapexit@spawn.link>  Mon, 20 May 2019 08:18:21 -0400
-
-mergerfs (2.26.2~debian-buster) buster; urgency=medium
-
-  * c21aa34 fix renaming over open unlinked file
-  * 810871d update support section of README
- -- trapexit <trapexit@spawn.link>  Thu, 16 May 2019 14:49:38 -0400
-
-mergerfs (2.26.1~debian-buster) buster; urgency=medium
-
-  * e052446 fix setting of fsname
- -- trapexit <trapexit@spawn.link>  Sun, 12 May 2019 14:30:37 -0400
-
-mergerfs (2.26.0~debian-buster) buster; urgency=medium
-
-  * 5883020 tweak docs
-  * 374580a create integration tests
-  * 825fcf7 cleanup and rework build system
-  * 3288d83 add reference to 32bit mmap kernel bug
-  * df0d055 fix outarg size calculation to accomidate newer fuse_kernel.h on older platforms
-  * 6ecc618 remove .fuse_hidden file creation
-  * 2a075ea fall back to other file copy methods in clonefile
-  * b69819e ioctl: don't set outbufsz when not needed
-  * 61d764d makefile: remove superclean dependency from tarball
-  * b0b265b parallel deb building
-  * 1be9900 rework makefiles for better manage parallel builds
-  * 38037fe replace {attr,entry,negative}_timeout references
-  * 2af0dcd fix building of deb pkg
-  * 0918dfd make attr, entry, negative_entry cache timeouts runtime configurable
-  * 95c0cc7 replace libfuse's autoconf with makefile
-  * 1d48dba add discord server to docs
-  * 7bf607b fix FS_IOC_{GET,SET}{FLAGS,VERSION} ioctl calls
-  * 940c323 misc fixes for FreeBSD
-  * 5ab7d2d remove O_LARGEFILE
-  * 65d3bfe remove libfuse modules
-  * 9d9ee7b general code cleanup
-  * c5b2415 remove `defaults`, hard code atomic_o_trunc, big_writes, and default_permissions
-  * f4a06ca fix <> escaping in argument list
-  * 5be7e00 add statfs cache
-  * 7a057da add policy cache for 'open'
-  * a13b822 add mention of noforget argument for NFS exports
-  * 9fd3b96 make ioctl on directories use open policy
-  * a57c680 clarify descriptions regarding funcitons and those without policies
-  * 3a12134 add more info on different caching techniques
-  * 87b2795 set direct_io per open/create, now runtime configurable
-  * 7eefb58 misc doc updates
-  * 0b5a0d1 update README regarding valid ENOSPC errors
-  * aa56e12 mention mv quirk in known issues
-  * 10f7f8b optimize link_cow eligibility check
-  * dfa1c1a check minfreespace on newest policy create
-  * 4096940 misc README updates
-  * 6de8e44 general cleanup of makefile, add static & lto building
-  * 6ae6846 fix building on alpine w/ musl
- -- trapexit <trapexit@spawn.link>  Fri, 10 May 2019 12:03:22 -0400
-
-mergerfs (2.25.1~debian-buster) buster; urgency=medium
-
-  * 7d9458f change xattr setting notsup to nosys
-  * 43b676a fix indexing of truncate targets
- -- trapexit <trapexit@spawn.link>  Mon, 19 Nov 2018 23:32:07 -0500
-
-mergerfs (2.25.0~debian-buster) buster; urgency=medium
-
-  * c46134c fix building on platforms without O_PATH
-  * 3631ab7 optimize readdir file dedup
-  * 680f819 add ability to change statfs behavior
-  * 7524e57 rename NW (no write) to NC (no create)
-  * b55ebba add tagging branches RW/RO/NW
-  * 8eacb00 misc updates to docs
-  * 9e0ab1f misc cleanups
-  * 8a48b74 policy return cleanup
-  * 85026d5 add FICLONE and copy_file_range to clonefile
-  * 1885a82 remove libattr dependency
-  * 65f482e add ability to turn on/off xattr support at runtime
-  * 8d1a156 fix building on certain platforms
-  * 93f7d7d add link_cow feature
-  * 9afefef keep literal when glob fails
-  * 340f3c8 add fuse to debian pkg dependency
-  * f856336 add security_capability option
-  * 057eafe add comment regarding tar error
-  * 587ab62 tweak docs, add FAQ regarding vendoring of libfuse
-  * 756d4ff Makefile: option for building with system libfuse
-  * ec6adaa options: move "-o threads=" help text to libfuse/
-  * fc52f89 add FAQ entry on hard links
-  * e987ff4 Makefile: don't touch LDFLAGS
-  * 39d5ab8 add details regarding use_ino
-  * c005463 change examples to use /mnt rather than /tmp
- -- trapexit <trapexit@spawn.link>  Tue, 6 Nov 2018 08:15:02 -0500
-
-mergerfs (2.24.2~debian-buster) buster; urgency=medium
-
-  * 73e8867 fix version.hpp creation, again
- -- trapexit <trapexit@spawn.link>  Sat, 24 Mar 2018 06:53:35 -0700
-
-mergerfs (2.24.1~debian-buster) buster; urgency=medium
-
-  * 24e690b fix versioning with tarball building
- -- trapexit <trapexit@spawn.link>  Fri, 16 Mar 2018 23:27:04 -0400
-
-mergerfs (2.24.0~debian-buster) buster; urgency=medium
-
-  * e521833 bump change date on readme and rebuild man
-  * 737d941 bump builtin libfuse version
-  * 9830e29 fix version generation
-  * cfe9c28 ignore clonepath metadata errors. fixes #470
-  * f48c16d stop clonepath at base directory. fixes #467
-  * 906702d add hard_remove details and comment on unionfs
-  * b4a003c Remove duplicate word in README
-  * b48054f add XRP to support list
-  * d8f05ac add BCH, ETH, & LTC donation addresses
-  * 7d93d59 add explination in FAQ about what mergerfs is / is not
-  * 7207e7f faq answer on epmfs
-  * f72bb1e update FAQ and misc tweaks
-  * bd14f33 add support info and how mergerfs works section
-  * 43d50ca Create issue_template.md
- -- trapexit <trapexit@spawn.link>  Fri, 9 Mar 2018 21:14:44 -0500
-
-mergerfs (2.23.1~debian-buster) buster; urgency=medium
-
-  * 1d6d227 call 32bit versions of set/geteuid on 32bit platforms
-  * 821d1b6 rebuild manpage
-  * ebe737e fixup makefile
-  * 9104f25 misc updates to docs
-  * c1d85ef expand support section of readme
-  * cc1f07e add info on creating a caching behavior
- -- trapexit <trapexit@spawn.link>  Tue, 17 Oct 2017 22:23:27 -0400
-
-mergerfs (2.23.0~debian-buster) buster; urgency=medium
-
-  * 75ed37a add setting of thread pool size
-  * 8043829 fixed threads
-  * 0708110 improve khash performance
-  * a7b126c use pragma once in headers
-  * a2bddec add 'ignore path preserving on rename' feature
-  * aea2b40 use temp files (then rename) when moving files for moveonenospc
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 3 Jul 2017 00:02:35 -0400
-
-mergerfs (2.22.1~debian-buster) buster; urgency=medium
-
-  * bfd410d use fusepath from fileinfo. closes #417
-  * 7296d3d add time.h to fix compiling on some platforms
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sat, 24 Jun 2017 17:04:37 -0400
-
-mergerfs (2.22.0~debian-buster) buster; urgency=medium
-
-  * de0985c add libfuse 2.9.7 to repo and build against libfuse.a
-  * 87c2f2f add nullrw feature to facilitate benchmarking
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 2 Jun 2017 16:14:54 -0400
-
-mergerfs (2.21.0~debian-buster) buster; urgency=medium
-
-  * 6a7675f symlinkify: file -> symlink-to-original-file after timeout
-  * 8ba3a08 make dropcacheonclose runtime configurable
-  * ccaa458 better handle incomplete reads/writes in copying files
-  * 162b99e enable nopath and nullpath_ok
-  * f15437c tweak movefile behavior
-  * 5f3aa6e add more travis build targets
-  * 2fbeb67 hide fs::fadvise as it's not used directly
-  * 8b976ab support older libfuse without utime_omit_ok flag
-  * 1a1fa06 fadvise cleanup
-  * 617195d enable utime_omit_ok flag
-  * be3eb7e work around getgrouplist signature difference on osx
-  * 0600734 handle 32bit and 64bit inode recalculation
-  * 9d0798d restructure fadvise
-  * e2acffe restructure fallocate abstraction
-  * 42d454a abstract futimesat
-  * 0b2bf17 abstract access to highres atime/mtime
-  * e20d566 use correct integer types
-  * 1539aca use compiler's preprocessor rather than cpp explicitly
-  * 9d799ff setup travis-ci
-  * c043ef9 make fs::attr return ENOTSUP on EINVAL #381
-  * 215f129 explicitly define path preservation, better explain move issues
-  * bb4ec91 fix incorrect section header syntax
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 5 May 2017 00:47:24 -0400
-
-mergerfs (2.20.0~debian-buster) buster; urgency=medium
-
-  * cf2cb54 add info on inodes running out to faq on filled drives
-  * 94ebccc try to clarify how path preserving policies work and other tweaks to docs
-  * 4e7e74d update docs to include dropcacheonclose and warn about directory mtime
-  * 6aa62d0 add option to drop file caches before closing files
-  * 492d895 check metadata on chown/chmod errors when cloning
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 13 Mar 2017 13:43:22 -0400
-
-mergerfs (2.19.0~debian-buster) buster; urgency=medium
-
-  * 9cc9bb9 misc document updates
-  * 16e7c72 update documentation, focus on explaining double caching & direct_io
-  * a60d815 add ifndefs to all headers
-  * e93c946 limit need to explicitly call .c_str()
-  * 7b4e1ea remove clone command
-  * 726b88e restructure error calculation
-  * d67d5de check for system.posix_acl_default before setting umask
-  * 1aa76a5 use different read and write functions when using direct_io
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 31 Jan 2017 19:24:48 -0500
-
-mergerfs (2.18.0~debian-buster) buster; urgency=medium
-
-  * 67b48fc compute inode in readdir
-  * b1459c6 only remove src/version.hpp if git repo and git available
-  * c8fa51c support setting of inodes (using use_ino option)
-  * 822204f replace std::set with klib's khash to increase readdir performance
-  * 5f7a168 note that mergerfs should be run as root
-  * 35075bb return clonepath errors
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 16 Dec 2016 11:16:59 -0500
-
-mergerfs (2.17.0~debian-buster) buster; urgency=medium
-
-  * 05d81db update manpage
-  * 3c5351a ignore filesystems which return zeros for statfs. closes #335
-  * 3d2283f clang cpp doesn't like grep exiting early
-  * 192bb9c remove usage of -D from install
-  * 157dae0 define O_LARGEFILE and O_NOATIME if needed
-  * 00c814d consolidate and simplify utime
-  * 6d6fb45 check if fdatasync is available and return ENOSYS if not
-  * 897f20b minor correction of spelling mistake
-  * d0b6cd1 further abstraction of system calls
-  * 1dc7bff wrap most posix filesystem functions
-  * 8f594e1 add flock
-  * cd90193 add some more explination to the FAQ
-  * cd71af8 add mergerfs.ctl and scorch to tooling section
-  * 3fb7f89 add EDQUOT to errors which trigger moveonenospc
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 15 Nov 2016 23:35:06 -0500
-
-mergerfs (2.16.1~debian-buster) buster; urgency=medium
-
-  * dfa8269 update manpage
-  * d9a7906 use SYS_setgroup32 syscall if available. closes #319
-  * b1f2e94 add information about page cache kernel panic bug
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 19 Sep 2016 17:07:42 -0400
-
-mergerfs (2.16.0~debian-buster) buster; urgency=medium
-
-  * a8cd9b7 recreate manpage
-  * 7e423cd small tweaks to build on Debian kFreeBSD
-  * 0395e7c fix futimes version of utimes wrapper
-  * 9392317 fix #define typo
-  * 1513c92 abstract posix_fadvise
-  * 158dda9 Fix minor typo
-  * 1a698e5 rename include cpp files to have icpp extension
-  * 2ee6b4f include sys/types.h to pick up ssize_t
-  * 709dda5 support systems without ENODATA
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Wed, 14 Sep 2016 08:50:51 -0400
-
-mergerfs (2.15.0~debian-buster) buster; urgency=medium
-
-  * 49474f0 make futimes crossplatform
-  * 34d38cb split sendfile wrapper into separate files
-  * 192a9d5 make fs_attr compile on unsupported platforms
-  * 40574bd use dynamic buffer for realpath
-  * 064fd55 bump FUSE_USE_VERSION to 29
-  * 45f757d add osx version of fallocate
-  * 0fceb8e add epall and eprand policies
-  * 7634eb1 replace nonstandard eaccess with POSIX.1-2008 faccessat
-  * 47f184a fix typo and clarify feature
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 7 Aug 2016 14:46:43 -0400
-
-mergerfs (2.14.0~debian-buster) buster; urgency=medium
-
-  * a93ab6c add existing path first found policy. closes #289
-  * 43cbd9c move size calculations to use uint64_t. fixes #287
-  * 9f36ead add license title
-  * 30cdfa6 reiterate path preserving policies
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 11 Jul 2016 20:50:32 -0400
-
-mergerfs (2.13.1~debian-buster) buster; urgency=medium
-
-  * 3cb2045 update man page
-  * 6dc6fde update info on mmap bug and when it was fixed
-  * cb35a37 rework fallocate logic
-  * 23b8e45 fix ioctl on directories
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 19 May 2016 17:30:07 -0400
-
-mergerfs (2.13.0~debian-buster) buster; urgency=medium
-
-  * 3a50344 update man page
-  * be6341e create eplus (existing path, least used space) policy. closes #273
-  * f7d3e8b create lus (least used space) policy. closes #273
-  * 74ed1b0 faq update on direct writes
-  * d0414d7 clean up information regarding fstab
-  * ef8d8f3 add fsname to readme
-  * cd15b7a further tweak language regarding policies wrt create category
-  * 8643d35 make policy descriptions more explicit
-  * 1cfe1c3 tweak doc language
-  * dbb13ef add details of mmap cache bug
-  * 90ca14a add dual mount suggestion to mmap problem
-  * cfaf812 add details regarding rtorrent, mmap, and direct_io
-  * 0c6c69e update readme to include mergerfs.dedup
-  * 309cfee change tar.gz build url
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sat, 7 May 2016 15:19:34 -0400
-
-mergerfs (2.12.3~debian-buster) buster; urgency=medium
-
-  * 382ca87 tweak man page creation
-  * 070ed08 properly check errors of xattr. closes #255
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 10 Mar 2016 18:43:12 -0500
-
-mergerfs (2.12.2~debian-buster) buster; urgency=medium
-
-  * 6492fda update precompiled man page
-  * 2061211 fix rename failing on non-path preserving policies
-  * 4928944 clarify that rename uses the create policy to make decisions
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 6 Mar 2016 02:23:47 -0500
-
-mergerfs (2.12.1~debian-buster) buster; urgency=medium
-
-  * 12cf57d re-add minfreespace check to epmfs policy
-  * 59b08a5 change tooling names and add guide link
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 4 Mar 2016 13:56:15 -0500
-
-mergerfs (2.12.0~debian-buster) buster; urgency=medium
-
-  * f4a4cc5 fix building of platform specific deb packages
-  * ef4c583 fix printing of versions with no changes
-  * 4ecf3c5 clearly separate usage of statvfs from stat for file existance
-  * 779143f add minfreespace checks to policy ff's create and remove fwfs
-  * 4d7148c update readme with minfreespace and readonly details
-  * 14886a2 add readonly and minfreespace filters to all policy for creates. closes #236
-  * 9819cf6 fix clonepath being called on wrong source
-  * c56b488 fix creation of changelog
-  * e593927 normalize error handling in rename policy
-  * 7c85cd9 ff policy tweaks
-  * 5cf3bb7 override standard libfuse version flag
-  * 25a0399 minor tweaks to filesystem utility functions
-  * 792c9b9 use stat/2 rather than statvfs/2 to find file drive
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 1 Mar 2016 00:19:59 -0500
-
-mergerfs (2.11.0~debian-buster) buster; urgency=medium
-
-  * a698a8a update / tweak readme
-  * d4ec341 remove unnecessary policies
-  * 5813d1e ignore drives mounted as readonly from create policies. closes #224
-  * a4e60d7 add eplfs info to readme
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 21 Feb 2016 18:31:02 -0500
-
-mergerfs (2.10.0~debian-buster) buster; urgency=medium
-
-  * f3e75a0 use stat.st_dev to uniquely identify mounts for statfs. closes #213
-  * b3248a8 simplify policies
-  * 7bf1ca4 add existing path, least free space policy. closes #216
-  * 6086620 use references to srcmounts rather than copies
-  * 3a51ceb add some "why mergerfs instead of X" sections to the FAQ
-  * 681f3d7 add section to readme about trash directories
-  * 0bac344 add information on fixing libfuse crashes
-  * 840f9b8 remove sbin from rpm spec file
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 15 Feb 2016 22:57:35 -0500
-
-mergerfs (2.9.1~debian-buster) buster; urgency=medium
-
-  * f779f82 fix statvfs drive dedup. closes #209
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 22 Jan 2016 10:16:01 -0500
-
-mergerfs (2.9.0~debian-buster) buster; urgency=medium
-
-  * 3e20adb remove clone test tool
-  * e285bde update the precompiled man page
-  * 853769b general tweaks to readme
-  * ea32575 make symlink function like mknod/mkdir. closes #204
-  * 1d1694b fix indexing of mknod targets. closes #202
-  * 93397ea remove incorrect warning about race condition with mkdir/mknod
-  * 25265f4 dedup based on full statvfs struct rather than fsid. closes #183
-  * de776b7 remove tooling from repo. closes #198
-  * 62f8fc5 have link act similar to rename
-  * 242af77 move from MIT to ISC license. closes #194
-  * 4c77ac4 all action functions return success should at least one succeed. closes #189
-  * b811522 update man page
-  * 8a5c524 replace srcpoints with srcmounts
-  * a3e6a03 rework rename algo to minimize likelihood of EXDEV being returned. closes #187
-  * eb6d1a1 change to using template for policy class
-  * 55c4a32 add artifacts to gitignore
-  * 51ae7d1 change make to work with non-bash shells
-  * c731b70 fix building without xattr
-  * 9e24796 add mergerfs pid to xattrs
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 21 Jan 2016 14:55:15 -0500
-
-mergerfs (2.8.0~debian-buster) buster; urgency=medium
-
-  * 5e880bd use SYS_setgroups rather than setgroups
-  * 8a651b0 add support section to readme and manpage
-  * 93cedab fix misc issues flagged by clang scan-build
-  * fd4ce1b include distro and codename in deb package versions
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 29 Oct 2015 23:38:45 -0400
-
-mergerfs (2.7.0~debian-buster) buster; urgency=medium
-
-  * f3a6876 remove pandoc from build requirements
-  * 30c29c9 remove manpage from root directory
-  * 8ed11a0 if pandoc doesn't exist copy premade version
-  * 46c8361 offer prebuilt manpage for platforms without easy access to pandoc
-  * 02df441 update info on Samba client EXDEV issue
-  * fe79609 fix typo
-  * 873c4a9 add link to gvfs-fuse patch
-  * 4df9b2e add documentation on SMB client EXDEV / EIO issue
-  * 1c7de2d fix minor integer casting issues
-  * a10de2a update build dependencies for Fedora
-  * 3d7d2cf add sbin directory to rpm spec
-  * 5489952 enhance git2debcl to work with older python releases
-  * 068fbc0 set rpm dependency to fuse
-  * 5a76c41 create mount.mergerfs symlink. closes #149
-  * 58446f9 misc fixes to compile on older platforms
-  * 40f569b rewrite gid cache system
-  * f6d396c audit (and fix) file permissions and ownership. closes #148
-  * 53e3284 remove usage of UINT32_MAX macro
-  * 09ffc8c provide usage text and version info. closes #146
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 18 Oct 2015 00:15:28 -0400
-
-mergerfs (2.6.0~debian-buster) buster; urgency=medium
-
-  * c289daf swap deb and unsigned-deb make tagets. closes #140
-  * 5808ab7 move on enospc when writing feature. closes #141
-  * 4e5578d make note about escaping glob tokens more explicit
-  * 4b375fa include rpm-build in Fedora dependencies
-  * 9542e63 include link to release page in readme
-  * de583b7 clean up options listing
-  * 8bf0f75 create summary feature section
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sat, 26 Sep 2015 14:14:04 -0400
-
-mergerfs (2.5.0~debian-buster) buster; urgency=medium
-
-  * 5850fbe bump README date
-  * a960a7e cleanup controlfile manipulation
-  * e0cf972 include default_permissions in default arguments
-  * f4e3f28 change README regarding setgroups cache and new rwlock ugid fallback
-  * 08c0c2d fix typo in README. closes #134
-  * 3163258 make changing credentials opportunistic + per thread setgroups cache
-  * b194272 add notes on permissions and errors to readme
-  * c131310 include known issues section to readme
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 14 Sep 2015 22:45:45 -0400
-
-mergerfs (2.4.0~debian-buster) buster; urgency=medium
-
-  * ce93529 realpath'ize all source mount points. closes #117
-  * 2d89947 fix non-suffixed setxattr of user.mergerfs.minfreespace
-  * e98b801 remove version.hpp on clean
-  * b22528b add user.mergerfs.version xattr
-  * e377d54 add user.mergerfs.policies xattr
-  * 08d07b7 add building of rpm
-  * 305f190 add basic instructions for building on Fedora
-  * 8178bf5 refactor and simplify getxattr for user.mergerfs.\*
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 6 Sep 2015 18:25:21 -0400
-
-mergerfs (2.3.0~debian-buster) buster; urgency=medium
-
-  * aea5e44 use correct variable for finding version
-  * bc77b0f add minfreespace check to epmfs create policy
-  * 1f1e481 rework rename
-  * 7a93198 forgot to add einval to policy
-  * bbc75f6 create errno policies for simulating errors. closes #107
-  * 126df0f fix epmfs failing to pick the existing path. closes #102
-  * ab68ac0 enhance deb building
-  * e73ea33 format README better for man pages
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Wed, 2 Sep 2015 08:02:26 -0400
-
-mergerfs (2.2.0~debian-buster) buster; urgency=medium
-
-  * 9519251 update README regarding options
-  * 8767db9 remove unused variable
-  * 267f2d2 move requesting of FUSE flags to init from cli args
-  * f130d07 config get and struct naming cleanup
-  * 52d8029 passthrough ioctl args without processing. closes #90
-  * c60d038 use gte rather than gt for mtime comparisons. fixes #91
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Wed, 5 Aug 2015 12:30:14 -0400
-
-mergerfs (2.1.2~debian-buster) buster; urgency=medium
-
-  * 80b2c35 add creation of full path for open
-  * 983fa91 change fuse functions to use the fuse namespace
-  * e5359eb remove unused readdir function
-  * f00cd14 use pthread_getugid_np instead of gete{u,g}id on OSX. fixes #84
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 16 Jul 2015 16:58:14 -0400
-
-mergerfs (2.1.1~debian-buster) buster; urgency=medium
-
-  * 4d60538 ignore ENOTSUP errors when cloning paths. fixes #82
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 13 Jul 2015 12:21:48 -0400
-
-mergerfs (2.1.0~debian-buster) buster; urgency=medium
-
-  * aafc1e9 add str to size_t conversion code
-  * b3109ac add minfreespace to xattr interface
-  * d079856 add info on lfs and fwfs policies and minfreespace option
-  * c101430 rework category -> fuse function table
-  * b2cd791 stop auto calculating and storing fullpath in policies
-  * 0c60701 create different policies based on category of use
-  * 51b6d3f add category to policies so as to distinguish between creates and searches
-  * 6ca4389 separate policies into individual modules
-  * 3c8f122 move policy function type from fs to policy
-  * 2bd4456 move Path object to separate file
-  * 8e5b796 create lfs policy. closes #73
-  * 58167c3 first w/ free space policy. closes #72
-  * ccb22c1 create minfreespace option. closes #71
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sun, 5 Jul 2015 21:03:34 -0400
-
-mergerfs (2.0.1~debian-buster) buster; urgency=medium
-
-  * ad7ce48 fix readme typos and misc formatting
-  * fe0f442 add Tips and FAQ section to readme
-  * 1879c9c update readme with defaults option info
-  * 45b73e5 fix calling of lgetxattr. closes #68
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 5 Jun 2015 20:13:24 -0400
-
-mergerfs (2.0.0~debian-buster) buster; urgency=medium
-
-  * 5fb2775 attempt to set priority to -10 on startup. closes #65
-  * 4b204b8 restrict who can setxattr the pseudo file. closes #64
-  * 33c837a provide 'defaults' option. closes #58
-  * 951b22b set FUSE subtype to 'mergerfs'. closes #59
-  * 74c334c add default policies for categories and description of `all`
-  * 08366a3 match cli options to xattrs
-  * 91671d7 remove FileInfo and keep only file descriptor
-  * c022741 revert removal of 'all' policy and relevant behavior. closes #54
-  * 12f393a per FUSE function policies. closes #52, #53
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 17 Mar 2015 20:40:35 -0400
-
-mergerfs (1.7.1~debian-buster) buster; urgency=medium
-
-  * 283a2b2 Try RLIMIT_INFINITY first, then cur = max, then loop and try to increase till error. closes #50
-  * 1a1c9db close file after getting ioc flags. closes #48
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 17 Feb 2015 11:48:55 -0500
-
-mergerfs (1.7.0~debian-buster) buster; urgency=medium
-
-  * d30cae2 add user.mergerfs.allpaths and user.mergerfs.relpath to getxattr
-  * 2e95c6e merge action and search category
-  * b411c63 Remove 'all' policy and simplify logic
-  * 1f90203 use standard platform macros. closes #43
-  * c2cbb93 elevate privileges when calling clonepath. closes #41
-  * 6276ce9 handle EEXIST while cloning a path. closes #40
-  * d1f3bd8 add clonepath tool
-  * 031b87f slight refactoring
-  * 5dd0729 remove longest common prefix from fsname. closes #38
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sat, 7 Feb 2015 18:49:36 -0500
-
-mergerfs (1.6.0~debian-buster) buster; urgency=medium
-
-  * 6c3ff01 pass const strings by reference. closes #33
-  * d7ede20 provide stat to readdir filler. closes #32
-  * 613b996 support RHEL6. closes #29
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 10 Nov 2014 21:11:17 -0500
-
-mergerfs (1.5.1~debian-buster) buster; urgency=medium
-
-  * 47522a2 exclude merges in the debian changelog. closes #28
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 29 Sep 2014 19:30:12 -0400
-
-mergerfs (1.5.0~debian-buster) buster; urgency=medium
-
-  * 075d62d add support for ioctl on directories. closes #27
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 26 Sep 2014 18:33:51 -0400
-
-mergerfs (1.4.1~debian-buster) buster; urgency=medium
-
-  * cfe7609 find functions now return errors. closes #24
-  * 8f35406 use lstat to confirm file existance instead of eaccess. closes #25
-  * 4ea1adb use f_frsize rather than f_bsize for mfs calculations. closes #26
-  * 15f8849 add custom git log to debian changelog script
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Sat, 23 Aug 2014 11:44:40 -0400
-
-mergerfs (1.4.0~debian-buster) buster; urgency=medium
-
-  * 7e9ccd0 support runtime setting of srcmounts. closes #12
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Fri, 8 Aug 2014 10:20:27 -0400
-
-mergerfs (1.3.0~debian-buster) buster; urgency=medium
-
-  * 992e05e update docs for xattr features
-  * b82db46 getxattr for user.mergerfs.{base,full}path returns the source paths. closes #23
-  * 7b0d703 only allow manipulation of runtime settings via xattrs. closes #22
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Thu, 31 Jul 2014 17:27:43 -0400
-
-mergerfs (1.2.4~debian-buster) buster; urgency=medium
-
-  * 45cec2d use int instead of long for ioctl. fixes #21
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 22 Jul 2014 07:07:25 -0400
-
-mergerfs (1.2.3~debian-buster) buster; urgency=medium
-
-  * 2295714 on ENOENT try cloning the dest path to source drive. closes #20
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 7 Jul 2014 21:54:38 -0400
-
-mergerfs (1.2.2~debian-buster) buster; urgency=medium
-
-  * ccb0ac1 generate the controlfile data on the fly. closes #19
-  * 15a0416 don't flush if it's the .mergerfs pseudo file. closes #18
-  * ec38a9c fix rename'ing to local device
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Wed, 25 Jun 2014 15:17:26 -0400
-
-mergerfs (1.2.1~debian-buster) buster; urgency=medium
-
-  * 3b6c748 use geteuid syscall as well
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 23 Jun 2014 12:17:09 -0400
-
-mergerfs (1.2.0~debian-buster) buster; urgency=medium
-
-  * f385f02 add -O2 to CFLAGS
-  * 0e12d79 platform specific code to deal with sete{u,g}id. closes #17
-  * 7164535 add flush callback. closes #16
-  * dd66607 add {read,write}_buf functions. closes #15
-  * 0f8fe47 add man file via markdown -> man conversion with pandoc. closes #14
-  * 45a2e09 reverse source and destination mount points to match fstab requirements. closes #13
-  * 645c823 source mount paths can contain globing. closes #10
-  * 1835826 fsname set to list of src mounts. closes #9
-  * 4a0bc4a add debian package building. closes #11
-  * e2e0359 fix free space calculations. closes #8
-  * 655436f further Makefile enhancements
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Wed, 18 Jun 2014 10:40:54 -0400
-
-mergerfs (1.1.0~debian-buster) buster; urgency=medium
-
-  * a7d9a9b enhance Makefile
-  * 36887e4 when readdir's filler returns non-zero return ENOMEM. closes #7
-  * 652a299 add fgetattr. closes #5
-  * aab90b0 rework policy code
-  * 345d0bb use eaccess to determine permissions for ffwp. closes #2
-  * 4c7095c remove stat'ing of files in readdir. closes #3
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Wed, 28 May 2014 21:39:07 -0400
-
-mergerfs (1.0.2~debian-buster) buster; urgency=medium
-
-  * bbc4b59 fs::make_path should check for forward slashes, add if missing
-  * 16fe0cf remove statfs policy
-  * 29ed2bc add FS_IOC_{GET}VERSION to ioctl
-  * 243a193 use long instead of int to limit possibility of overflow in switch, closes #1
-  * 97ce6f5 use {get,list,set}xattr to modify runtime
-  * 428dd8c update build instructions in readme
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Tue, 27 May 2014 07:17:32 -0400
-
-mergerfs (1.0.1~debian-buster) buster; urgency=medium
-
-  * 7f640c4 fix building without libattr
- -- Antonio SJ Musumeci <trapexit@spawn.link>  Mon, 19 May 2014 09:34:31 -0400
-
diff --git a/README.md b/README.md
index f767cfb..0d0f76a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 % mergerfs(1) mergerfs user manual
 % Antonio SJ Musumeci <trapexit@spawn.link>
-% 2021-10-25
+% 2023-01-16
 
 # NAME
 
@@ -14,7 +14,9 @@ mergerfs -o&lt;options&gt; &lt;branches&gt; &lt;mountpoint&gt;
 
 # DESCRIPTION
 
-**mergerfs** is a union filesystem geared towards simplifying storage and management of files across numerous commodity storage devices. It is similar to **mhddfs**, **unionfs**, and **aufs**.
+**mergerfs** is a union filesystem geared towards simplifying storage
+and management of files across numerous commodity storage devices. It
+is similar to **mhddfs**, **unionfs**, and **aufs**.
 
 
 # FEATURES
@@ -36,7 +38,10 @@ mergerfs -o&lt;options&gt; &lt;branches&gt; &lt;mountpoint&gt;
 
 # HOW IT WORKS
 
-mergerfs logically merges multiple paths together. Think a union of sets. The file/s or directory/s acted on or presented through mergerfs are based on the policy chosen for that particular action. Read more about policies below.
+mergerfs logically merges multiple paths together. Think a union of
+sets. The file/s or directory/s acted on or presented through mergerfs
+are based on the policy chosen for that particular action. Read more
+about policies below.
 
 ```
 A         +      B        =       C
@@ -59,7 +64,12 @@ A         +      B        =       C
                                   +-- file6
 ```
 
-mergerfs does **NOT** support the copy-on-write (CoW) or whiteout behaviors found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **NOT** split data across drives. It is not RAID0 / striping. It is simply a union of other filesystems.
+mergerfs does **NOT** support the copy-on-write (CoW) or whiteout
+behaviors found in **aufs** and **overlayfs**. You can **not** mount a
+read-only filesystem and write to it. However, mergerfs will ignore
+read-only drives when creating new files so you can mix read-write and
+read-only drives. It also does **NOT** split data across drives. It is
+not RAID0 / striping. It is simply a union of other filesystems.
 
 
 # TERMINOLOGY
@@ -75,15 +85,16 @@ mergerfs does **NOT** support the copy-on-write (CoW) or whiteout behaviors foun
 
 # BASIC SETUP
 
-If you don't already know that you have a special use case then just start with one of the following option sets.
+If you don't already know that you have a special use case then just
+start with one of the following option sets.
 
 #### You need `mmap` (used by rtorrent and many sqlite3 base software)
 
-`allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs`
+`allow_other,cache.files=partial,dropcacheonclose=true,category.create=mfs`
 
 #### You don't need `mmap`
 
-`allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs`
+`allow_other,cache.files=off,dropcacheonclose=true,category.create=mfs`
 
 
 See the mergerfs [wiki for real world deployments](https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments) for comparisons / ideas.
@@ -91,54 +102,143 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
 
 # OPTIONS
 
-These options are the same regardless you use them with the `mergerfs` commandline program, used in fstab, or in a config file.
+These options are the same regardless of whether you use them with the `mergerfs` commandline program, in fstab, or in a config file.
 
 ### mount options
 
-* **config**: Path to a config file. Same arguments as below in key=val / ini style format.
+* **config**: Path to a config file. Same arguments as below in
+  key=val / ini style format.
 * **branches**: Colon delimited list of branches.
-* **allow_other**: A libfuse option which allows users besides the one which ran mergerfs to see the filesystem. This is required for most use-cases.
-* **minfreespace=SIZE**: The minimum space value used for creation policies. Can be overridden by branch specific option. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
-* **moveonenospc=BOOL|POLICY**: When enabled if a **write** fails with **ENOSPC** (no space left on device) or **EDQUOT** (disk quota exceeded) the policy selected will run to find a new location for the file. An attempt to move the file to that branch will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false, true = mfs)
-* **use_ino**: Causes mergerfs to supply file/directory inodes rather than libfuse. While not a default it is recommended it be enabled so that linked files share the same inode value.
-* **inodecalc=passthrough|path-hash|devino-hash|hybrid-hash**: Selects the inode calculation algorithm. (default: hybrid-hash)
-* **dropcacheonclose=BOOL**: When a file is requested to be closed call `posix_fadvise` on it first to instruct the kernel that we no longer need the data and it can drop its cache. Recommended when **cache.files=partial|full|auto-full** to limit double caching. (default: false)
-* **symlinkify=BOOL**: When enabled and a file is not writable and its mtime or ctime is older than **symlinkify_timeout** files will be reported as symlinks to the original files. Please read more below before using. (default: false)
-* **symlinkify_timeout=UINT**: Time to wait, in seconds, to activate the **symlinkify** behavior. (default: 3600)
-* **nullrw=BOOL**: Turns reads and writes into no-ops. The request will succeed but do nothing. Useful for benchmarking mergerfs. (default: false)
-* **ignorepponrename=BOOL**: Ignore path preserving on rename. Typically rename and link act differently depending on the policy of `create` (read below). Enabling this will cause rename and link to always use the non-path preserving behavior. This means files, when renamed or linked, will stay on the same drive. (default: false)
-* **security_capability=BOOL**: If false return ENOATTR when xattr security.capability is queried. (default: true)
-* **xattr=passthrough|noattr|nosys**: Runtime control of xattrs. Default is to passthrough xattr requests. 'noattr' will short circuit as if nothing exists. 'nosys' will respond with ENOSYS as if xattrs are not supported or disabled. (default: passthrough)
-* **link_cow=BOOL**: When enabled if a regular file is opened which has a link count > 1 it will copy the file to a temporary file and rename over the original. Breaking the link and providing a basic copy-on-write function similar to cow-shell. (default: false)
-* **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base)
-* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to ignore available space for branches mounted or tagged as 'read-only' or 'no create'. 'nc' will ignore available space for branches tagged as 'no create'. (default: none)
-* **nfsopenhack=off|git|all**: A workaround for exporting mergerfs over NFS where there are issues with creating files for write while setting the mode to read-only. (default: off)
-* **follow-symlinks=never|directory|regular|all**: Turns symlinks into what they point to. (default: never)
-* **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**: When a link fails with EXDEV optionally create a symlink to the file instead.
-* **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename fails with EXDEV optionally move the file to a special directory and symlink to it.
-* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false)
-* **async_read=BOOL**: Perform reads asynchronously. If disabled or unavailable the kernel will ensure there is at most one pending read request per file handle and will attempt to order requests by offset. (default: true)
-* **fuse_msg_size=UINT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
-* **threads=INT**: Number of threads to use in multithreaded mode. When set to zero it will attempt to discover and use the number of logical cores. If the lookup fails it will fall back to using 4. If the thread count is set negative it will look up the number of cores then divide by the absolute value. ie. threads=-2 on an 8 core machine will result in 8 / 2 = 4 threads. There will always be at least 1 thread. NOTE: higher number of threads increases parallelism but usually decreases throughput. (default: 0)
-* **fsname=STR**: Sets the name of the filesystem as seen in **mount**, **df**, etc. Defaults to a list of the source paths concatenated together with the longest common prefix removed.
-* **func.FUNC=POLICY**: Sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest**
-* **category.action=POLICY**: Sets policy of all FUSE functions in the action category. (default: epall)
-* **category.create=POLICY**: Sets policy of all FUSE functions in the create category. (default: epmfs)
-* **category.search=POLICY**: Sets policy of all FUSE functions in the search category. (default: ff)
-* **cache.open=UINT**: 'open' policy cache timeout in seconds. (default: 0)
-* **cache.statfs=UINT**: 'statfs' cache timeout in seconds. (default: 0)
-* **cache.attr=UINT**: File attribute cache timeout in seconds. (default: 1)
-* **cache.entry=UINT**: File name lookup cache timeout in seconds. (default: 1)
-* **cache.negative_entry=UINT**: Negative file name lookup cache timeout in seconds. (default: 0)
-* **cache.files=libfuse|off|partial|full|auto-full**: File page caching mode (default: libfuse)
-* **cache.writeback=BOOL**: Enable kernel writeback caching (default: false)
-* **cache.symlinks=BOOL**: Cache symlinks (if supported by kernel) (default: false)
-* **cache.readdir=BOOL**: Cache readdir (if supported by kernel) (default: false)
-* **direct_io**: deprecated - Bypass page cache. Use `cache.files=off` instead. (default: false)
-* **kernel_cache**: deprecated - Do not invalidate data cache on file open. Use `cache.files=full` instead. (default: false)
-* **auto_cache**: deprecated - Invalidate data cache if file mtime or size change. Use `cache.files=auto-full` instead. (default: false)
-* **async_read**: deprecated - Perform reads asynchronously. Use `async_read=true` instead.
-* **sync_read**: deprecated - Perform reads synchronously. Use `async_read=false` instead.
+* **allow_other**: A libfuse option which allows users besides the one
+  which ran mergerfs to see the filesystem. This is required for most
+  use-cases.
+* **minfreespace=SIZE**: The minimum space value used for creation
+  policies. Can be overridden by branch specific option. Understands
+  'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte
+  respectively. (default: 4G)
+* **moveonenospc=BOOL|POLICY**: When enabled if a **write** fails with
+  **ENOSPC** (no space left on device) or **EDQUOT** (disk quota
+  exceeded) the policy selected will run to find a new location for
+  the file. An attempt to move the file to that branch will occur
+  (keeping all metadata possible) and if successful the original is
+  unlinked and the write retried. (default: false, true = mfs)
+* **inodecalc=passthrough|path-hash|devino-hash|hybrid-hash**: Selects
+  the inode calculation algorithm. (default: hybrid-hash)
+* **dropcacheonclose=BOOL**: When a file is requested to be closed
+  call `posix_fadvise` on it first to instruct the kernel that we no
+  longer need the data and it can drop its cache. Recommended when
+  **cache.files=partial|full|auto-full** to limit double
+  caching. (default: false)
+* **symlinkify=BOOL**: When enabled and a file is not writable and its
+  mtime or ctime is older than **symlinkify_timeout** files will be
+  reported as symlinks to the original files. Please read more below
+  before using. (default: false)
+* **symlinkify_timeout=UINT**: Time to wait, in seconds, to activate
+  the **symlinkify** behavior. (default: 3600)
+* **nullrw=BOOL**: Turns reads and writes into no-ops. The request
+  will succeed but do nothing. Useful for benchmarking
+  mergerfs. (default: false)
+* **ignorepponrename=BOOL**: Ignore path preserving on
+  rename. Typically rename and link act differently depending on the
+  policy of `create` (read below). Enabling this will cause rename and
+  link to always use the non-path preserving behavior. This means
+  files, when renamed or linked, will stay on the same
+  drive. (default: false)
+* **security_capability=BOOL**: If false return ENOATTR when xattr
+  security.capability is queried. (default: true)
+* **xattr=passthrough|noattr|nosys**: Runtime control of
+  xattrs. Default is to passthrough xattr requests. 'noattr' will
+  short circuit as if nothing exists. 'nosys' will respond with ENOSYS
+  as if xattrs are not supported or disabled. (default: passthrough)
+* **link_cow=BOOL**: When enabled if a regular file is opened which
+  has a link count > 1 it will copy the file to a temporary file and
+  rename over the original. Breaking the link and providing a basic
+  copy-on-write function similar to cow-shell. (default: false)
+* **statfs=base|full**: Controls how statfs works. 'base' means it
+  will always use all branches in statfs calculations. 'full' is in
+  effect path preserving and only includes drives where the path
+  exists. (default: base)
+* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to
+  ignore available space for branches mounted or tagged as 'read-only'
+  or 'no create'. 'nc' will ignore available space for branches tagged
+  as 'no create'. (default: none)
+* **nfsopenhack=off|git|all**: A workaround for exporting mergerfs
+  over NFS where there are issues with creating files for write while
+  setting the mode to read-only. (default: off)
+* **follow-symlinks=never|directory|regular|all**: Turns symlinks into
+  what they point to. (default: never)
+* **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**:
+  When a link fails with EXDEV optionally create a symlink to the file
+  instead.
+* **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename
+  fails with EXDEV optionally move the file to a special directory and
+  symlink to it.
+* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel
+  and underlying filesystem). (default: false)
+* **async_read=BOOL**: Perform reads asynchronously. If disabled or
+  unavailable the kernel will ensure there is at most one pending read
+  request per file handle and will attempt to order requests by
+  offset. (default: true)
+* **fuse_msg_size=UINT**: Set the max number of pages per FUSE
+  message. Only available on Linux >= 4.20 and ignored
+  otherwise. (min: 1; max: 256; default: 256)
+* **threads=INT**: Number of threads to use. When used alone
+  (`process-thread-count=-1`) it sets the number of threads reading
+  and processing FUSE messages. When used together it sets the number
+  of threads reading from FUSE. When set to zero it will attempt to
+  discover and use the number of logical cores. If the thread count is
+  set negative it will look up the number of cores then divide by the
+  absolute value. ie. threads=-2 on an 8 core machine will result in 8
+  / 2 = 4 threads. There will always be at least 1 thread. If set to
+  -1 in combination with `process-thread-count` then it will try to
+  pick reasonable values based on CPU thread count. NOTE: higher
+  number of threads increases parallelism but usually decreases
+  throughput. (default: 0)
+* **read-thread-count=INT**: Alias for `threads`.
+* **process-thread-count=INT**: Enables separate thread pool to
+  asynchronously process FUSE requests. In this mode
+  `read-thread-count` refers to the number of threads reading FUSE
+  messages which are dispatched to process threads. -1 means disabled
+  otherwise acts like `read-thread-count`. (default: -1)
+* **fsname=STR**: Sets the name of the filesystem as seen in
+  **mount**, **df**, etc. Defaults to a list of the source paths
+  concatenated together with the longest common prefix removed.
+* **func.FUNC=POLICY**: Sets the specific FUSE function's policy. See
+  below for the list of value types. Example: **func.getattr=newest**
+* **category.action=POLICY**: Sets policy of all FUSE functions in the
+  action category. (default: epall)
+* **category.create=POLICY**: Sets policy of all FUSE functions in the
+  create category. (default: epmfs)
+* **category.search=POLICY**: Sets policy of all FUSE functions in the
+  search category. (default: ff)
+* **cache.open=UINT**: 'open' policy cache timeout in
+  seconds. (default: 0)
+* **cache.statfs=UINT**: 'statfs' cache timeout in seconds. (default:
+  0)
+* **cache.attr=UINT**: File attribute cache timeout in
+  seconds. (default: 1)
+* **cache.entry=UINT**: File name lookup cache timeout in
+  seconds. (default: 1)
+* **cache.negative_entry=UINT**: Negative file name lookup cache
+  timeout in seconds. (default: 0)
+* **cache.files=libfuse|off|partial|full|auto-full**: File page
+  caching mode (default: libfuse)
+* **cache.writeback=BOOL**: Enable kernel writeback caching (default:
+  false)
+* **cache.symlinks=BOOL**: Cache symlinks (if supported by kernel)
+  (default: false)
+* **cache.readdir=BOOL**: Cache readdir (if supported by kernel)
+  (default: false)
+* **direct_io**: deprecated - Bypass page cache. Use `cache.files=off`
+  instead. (default: false)
+* **kernel_cache**: deprecated - Do not invalidate data cache on file
+  open. Use `cache.files=full` instead. (default: false)
+* **auto_cache**: deprecated - Invalidate data cache if file mtime or
+  size change. Use `cache.files=auto-full` instead. (default: false)
+* **async_read**: deprecated - Perform reads asynchronously. Use
+  `async_read=true` instead.
+* **sync_read**: deprecated - Perform reads synchronously. Use
+  `async_read=false` instead.
 
 
 **NOTE:** Options are evaluated in the order listed so if the options are **func.rmdir=rand,category.action=ff** the **action** category setting will override the **rmdir** setting.
@@ -181,7 +281,7 @@ To make it easier to include multiple branches mergerfs supports [globbing](http
 
 
 ```
-# mergerfs -o allow_other,use_ino /mnt/disk\*:/mnt/cdrom /media/drives
+# mergerfs -o allow_other /mnt/disk\*:/mnt/cdrom /media/drives
 ```
 
 The above line will use all mount points in /mnt prefixed with **disk** and the **cdrom**.
@@ -190,7 +290,7 @@ To have the pool mounted at boot or otherwise accessible from related tools use
 
 ```
 # <file system>        <mount point>  <type>         <options>             <dump>  <pass>
-/mnt/disk*:/mnt/cdrom  /mnt/pool      fuse.mergerfs  allow_other,use_ino   0       0
+/mnt/disk*:/mnt/cdrom  /mnt/pool      fuse.mergerfs  allow_other           0       0
 ```
 
 **NOTE:** the globbing is done at mount or when updated using the runtime API. If a new directory is added matching the glob after the fact it will not be automatically included.
@@ -231,7 +331,7 @@ Note that this does *not* affect the inode that libfuse
 and the kernel use internally (also called the "nodeid").
 ```
 
-In the future the `use_ino` option will probably be removed as this feature should replace the original libfuse inode calculation strategy. Currently you still need to use `use_ino` in order to enable `inodecalc`.
+As of version 2.35.0 the `use_ino` option has been removed. mergerfs should always be managing inode values.
 
 
 ### fuse_msg_size
@@ -324,7 +424,7 @@ NFS is not fully POSIX compliant and historically certain behaviors, such as ope
 
 This hack addresses the issue where the creation of a file with a read-only mode but with a read/write or write only flag. Normally this is perfectly valid but NFS chops the one open call into multiple calls. Exactly how it is translated depends on the configuration and versions of the NFS server and clients but it results in a permission error because a normal user is not allowed to open a read-only file as writable.
 
-Even though it's a more niche situation this hack breaks normal security and behavior and as such is `off` by default. If set to `git` it will only perform the hack when the path in question includes `/.git/`. `all` will result it it applying anytime a readonly file which is empty is opened for writing.
+Even though it's a more niche situation this hack breaks normal security and behavior and as such is `off` by default. If set to `git` it will only perform the hack when the path in question includes `/.git/`. `all` will result it applying anytime a readonly file which is empty is opened for writing.
 
 
 # FUNCTIONS, CATEGORIES and POLICIES
@@ -367,7 +467,7 @@ Policies basically search branches and create a list of files / paths for functi
 
 Policies may have their own additional filtering such as those that require existing paths to be present.
 
-If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch. **ENOENT** will be returned if no elegible branch is found.
+If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch. **ENOENT** will be returned if no eligible branch is found.
 
 
 #### Path Preservation
@@ -566,7 +666,7 @@ make LTO=1            - build with link time optimization
 
 mergerfs can be upgraded live by mounting on top of the previous instance. Simply install the new version of mergerfs and follow the instructions below.
 
-Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
+Run mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
 
 ```
 $ sudo mount /mnt/mergerfs
@@ -739,7 +839,7 @@ MergerFS does not natively support any sort of tiered caching. Most users have n
 1. Fast network, slow drives, many readers: You've a 10+Gbps network with many readers and your regular drives can't keep up.
 2. Fast network, slow drives, small'ish bursty writes: You have a 10+Gbps network and wish to transfer amounts of data less than your cache drive but wish to do so quickly.
 
-With #1 its arguable if you should be using mergerfs at all. RAID would probably be the better solution. If you're going to use mergerfs there are other tactics that may help: spreading the data across drives (see the mergerfs.dup tool) and setting `func.open=rand`, using `symlinkify`, or using dm-cache or a similar technology to add tiered cache to the underlying device.
+With #1 it's arguable if you should be using mergerfs at all. RAID would probably be the better solution. If you're going to use mergerfs there are other tactics that may help: spreading the data across drives (see the mergerfs.dup tool) and setting `func.open=rand`, using `symlinkify`, or using dm-cache or a similar technology to add tiered cache to the underlying device.
 
 With #2 one could use dm-cache as well but there is another solution which requires only mergerfs and a cronjob.
 
@@ -879,12 +979,11 @@ echo 3 | sudo tee /proc/sys/vm/drop_caches
 
 * This document is very literal and thorough. Unless there is a bug things work as described. If a suspected feature isn't mentioned it doesn't exist. If certain libfuse arguments aren't listed they probably shouldn't be used.
 * Ensure you're using the latest version. Few distros have the latest version.
-* **use_ino** will only work when used with mergerfs 2.18.0 and above.
 * Run mergerfs as `root` (with **allow_other**) unless you're merging paths which are owned exclusively and fully by the same user otherwise strange permission issues may arise. mergerfs is designed and intended to be run as `root`.
 * If you don't see some directories and files you expect, policies seem to skip branches, you get strange permission errors, etc. be sure the underlying filesystems' permissions are all the same. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
 * If you still have permission issues be sure you are using POSIX ACL compliant filesystems. mergerfs doesn't generally make exceptions for FAT, NTFS, or other non-POSIX filesystem.
 * Do **not** use `cache.files=off` if you expect applications (such as rtorrent) to use [mmap](http://linux.die.net/man/2/mmap) files. Shared mmap is not currently supported in FUSE w/ page caching disabled. Enabling `dropcacheonclose` is recommended when `cache.files=partial|full|auto-full`.
-* [Kodi](http://kodi.tv), [Plex](http://plex.tv), [Subsonic](http://subsonic.org), etc. can use directory [mtime](http://linux.die.net/man/2/stat) to more efficiently determine whether to scan for new content rather than simply performing a full scan. If using the default **getattr** policy of **ff** it's possible those programs will miss an update on account of it returning the first directory found's **stat** info and its a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
+* [Kodi](http://kodi.tv), [Plex](http://plex.tv), [Subsonic](http://subsonic.org), etc. can use directory [mtime](http://linux.die.net/man/2/stat) to more efficiently determine whether to scan for new content rather than simply performing a full scan. If using the default **getattr** policy of **ff** it's possible those programs will miss an update on account of it returning the first directory found's **stat** info and it's a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
 * Some policies mixed with some functions may result in strange behaviors. Not that some of these behaviors and race conditions couldn't happen outside **mergerfs** but that they are far more likely to occur on account of the attempt to merge together multiple sources of data which could be out of sync due to the different policies.
 * For consistency its generally best to set **category** wide policies rather than individual **func**'s. This will help limit the confusion of tools such as [rsync](http://linux.die.net/man/1/rsync). However, the flexibility is there if needed.
 
@@ -936,7 +1035,6 @@ NFS does not like out of band changes. That is especially true of inode values.
 Be sure to use the following options:
 
 * noforget
-* use_ino
 * inodecalc=path-hash
 
 
@@ -983,10 +1081,6 @@ This is the same issue as with Samba. `rename` returns `EXDEV` (in our case that
 
 To create a `$topdir/.Trash` directory as defined in the standard use the [mergerfs-tools](https://github.com/trapexit/mergerfs-tools) tool `mergerfs.mktrash`.
 
-#### tar: Directory renamed before its status could be extracted
-
-Make sure to use the `use_ino` option.
-
 
 #### Supplemental user groups
 
@@ -1060,7 +1154,7 @@ Depends on what features you want. Generally speaking there are no "wrong" setti
 
 That said, for the average person, the following should be fine:
 
-`-o use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs`
+`-o cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs`
 
 
 #### Why are all my files ending up on 1 drive?!
@@ -1074,7 +1168,7 @@ This catches a lot of new users off guard but changing the default would break t
 
 #### Do hardlinks work?
 
-Yes. You need to use `use_ino` to support proper reporting of inodes but they work regardless. See also the option `inodecalc`.
+Yes. See also the option `inodecalc` for how inode values are calculated.
 
 What mergerfs does not do is fake hard links across branches.  Read the section "rename & link" for how it works.
 
@@ -1083,7 +1177,7 @@ Remember that hardlinks will NOT work across devices. That includes between the
 
 #### Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
 
-Yes. They are completely unreleated pieces of software.
+Yes. They are completely unrelated pieces of software.
 
 
 #### Can mergerfs run via Docker, Podman, Kubernetes, etc.
@@ -1111,6 +1205,32 @@ Whenever you run into a split permission issue (seeing some but not all files) t
 If using a network filesystem such as NFS, SMB, CIFS (Samba) be sure to pay close attention to anything regarding permissioning and users. Root squashing and user translation for instance has bitten a few mergerfs users. Some of these also affect the use of mergerfs from container platforms such as Docker.
 
 
+#### Why use FUSE? Why not a kernel based solution?
+
+As with any two solutions to a problem there are advantages and disadvantages to each one.
+
+A FUSE based solution has all the downsides of FUSE:
+
+* Higher IO latency due to the trips in and out of kernel space
+* Higher general overhead due to trips in and out of kernel space
+* Double caching when using page caching
+* Misc limitations due to FUSE's design
+
+But FUSE also has a lot of upsides:
+
+* Easier to offer a cross platform solution
+* Easier forward and backward compatibility
+* Easier updates for users
+* Easier and faster release cadence
+* Allows more flexibility in design and features
+* Overall easier to write, secure, and maintain
+* Ability to run without root access or need to change the kernel
+* Much lower barrier to entry (getting code into the kernel takes a lot of time and effort initially)
+
+
+FUSE was chosen because of all the advantages listed above. The negatives of FUSE do not outweigh the positives.
+
+
 #### Is my OS's libfuse needed for mergerfs to work?
 
 No. Normally `mount.fuse` is needed to get mergerfs (or any FUSE filesystem to mount using the `mount` command but in vendoring the libfuse library the `mount.fuse` app has been renamed to `mount.mergerfs` meaning the filesystem type in `fstab` can simply be `mergerfs`. That said there should be no harm in having it installed and continuing to using `fuse.mergerfs` as the type in `/etc/fstab`.
@@ -1219,7 +1339,6 @@ Yes, however if you do anything which may changes files out of band (including f
 Be sure to use the following options:
 
 * noforget
-* use_ino
 * inodecalc=path-hash
 
 
@@ -1265,6 +1384,8 @@ Filesystems are complex and difficult to debug. mergerfs, while being just a pro
 
 **Please make sure you are using the [latest release](https://github.com/trapexit/mergerfs/releases) or have tried it in comparison. Old versions, which are often included in distros like Debian and Ubuntu, are not ever going to be updated and your bug may have been addressed already.**
 
+**For commercial support or feature requests please contact me directly.**
+
 
 #### Information to include in bug reports
 
@@ -1294,37 +1415,9 @@ Filesystems are complex and difficult to debug. mergerfs, while being just a pro
 
 #### Support development
 
-This software is free to use and released under a very liberal license (ISC). That said if you like this software and would like to support its development donations are welcome.
-
-Crypto is fine in whatever protocol you prefer. My preferences for fiat would be GitHub Sponsors or PayPal though feel free to use any platform listed below.
-
-* GitHub Sponsors: https://github.com/sponsors/trapexit
-* PayPal: https://paypal.me/trapexit
-* Patreon: https://www.patreon.com/trapexit
-* BuyMeACoffee: https://buymeacoff.ee/trapexit
-* Ko-Fi: https://ko-fi.com/trapexit
-* Open Collective: https://opencollective.com/trapexit
-* Bitcoin (BTC): bc1qu537hqlnmn2wawx9n7nws0dlkz55h0cd93ny28
-* Bitcoin Cash (BCH): bitcoincash:qqp0vh9v44us74gaggwjfv9y54zfjmmd7srlqxa3xt
-* Bitcoin SV (BSV): 1FkFuxRtt3f8LbkpeUKRZq7gKJFzGSGgZV
-* Bitcoin Gold (BTG): AaPuJgJeohPjkB3LxJM6NKGnaHoRJ8ieT3
-* Litecoin (LTC): MJQzsHBdNnkyGqCFdcAdHYKugicBmfAXfQ
-* Dogecoin (DOGE): DLJNLVe28vZ4SMQSxDJLBQBv57rGtUoWFh
-* Ethereum (ETH): 0xB8d6d55c0319aacC327860d13f891427caEede7a
-* Any ERC20 Token: 0xB8d6d55c0319aacC327860d13f891427caEede7a
-* Ethereum Classic (ETC): 0x2B6054428e69a1201B6555f7a2aEc0Fba01EAD9F
-* Harmony (ONE): one1hrtd2hqrrx4vcvncvrgnlzg5yl9wahn66lq6rw (0xB8d6d55c0319aacC327860d13f891427caEede7a)
-* Monero (XMR): 45BBZMrJwPSaFwSoqLVNEggWR2BJJsXxz7bNz8FXnnFo3GyhVJFSCrCFSS7zYwDa9r1TmFmGMxQ2HTntuc11yZ9q1LeCE8f
-* Dash (DASH): XvsFrohu8tbjA4E8p7xsc86E2ADxLHGXHL
-* Chia (XCH): xch18l7e2q34jtuyzkq0jg8vp7yrtnfwzly30w3yyhkk96um2ur4yqcq6p2een
-* LBRY Credits (LBC): bFusyoZPkSuzM2Pr8mcthgvkymaosJZt5r
-* Ripple (XRP): r9f6aoxaGD8aymxqH89Ke1PCUPkNiFdZZC
-* Tezos (XTZ): tz1ZxerkbbALsuU9XGV9K9fFpuLWnKAGfc1C
-* Zcash (ZEC): t1Zo1GGn2T3GrhKvgdtnTsTnWu6tCPaCaHG
-* DigiByte (DGB): Sb8r1qTrryY9Sp4YkTE1eeKEGVzgArnE5N
-* Namecoin (NMC): NDzb9FkoptGu5QbgetCkodJqo2zE1cTwyb
-* Vertcoin (VTC): 3PYdhokAGXJwWrwHRoTywxG4iUDk6EHjKe
-* Other crypto currencies: contact me for address
+This software is released under the very liberal ISC license and is therefore free to use for personal or commercial uses. That said if you like this software and have the means please consider supporting its development.
+
+https://github.com/trapexit/support
 
 
 # LINKS
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 74f516b..0000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-2.33.5
\ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
index b4c8337..71bda37 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+mergerfs (2.34.1+git20230116.1.2d8d0f7-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 20 Jan 2023 09:42:34 -0000
+
 mergerfs (2.33.5-1) unstable; urgency=medium
 
   * New upstream version 2.33.5
diff --git a/debian/patches/0001-Use-the-debian-build-flags.patch b/debian/patches/0001-Use-the-debian-build-flags.patch
index 13a0dc6..507fd2e 100644
--- a/debian/patches/0001-Use-the-debian-build-flags.patch
+++ b/debian/patches/0001-Use-the-debian-build-flags.patch
@@ -9,9 +9,11 @@ build flags from the Debian build system are in use
  Makefile | 5 +++++
  1 file changed, 5 insertions(+)
 
---- a/Makefile
-+++ b/Makefile
-@@ -57,6 +57,9 @@
+Index: mergerfs.git/Makefile
+===================================================================
+--- mergerfs.git.orig/Makefile
++++ mergerfs.git/Makefile
+@@ -57,6 +57,9 @@ else
  LTO_FLAGS :=
  endif
  
@@ -21,7 +23,7 @@ build flags from the Debian build system are in use
  SRC	    = $(wildcard src/*.cpp)
  OBJS        = $(SRC:src/%.cpp=build/.src/%.o)
  DEPS        = $(SRC:src/%.cpp=build/.src/%.d)
-@@ -74,6 +77,7 @@
+@@ -74,6 +77,7 @@ CXXFLAGS    := \
                -std=c++11 \
                $(STATIC_FLAGS) \
                $(LTO_FLAGS) \
@@ -29,7 +31,7 @@ build flags from the Debian build system are in use
                -Wall \
                -Wno-unused-result \
                -MMD
-@@ -90,6 +94,7 @@
+@@ -90,6 +94,7 @@ TESTS_FLAGS = \
  
  LDFLAGS := \
      ${LDFLAGS} \
diff --git a/debian/patches/0002-Override-the-assignment-of-the-variable.patch b/debian/patches/0002-Override-the-assignment-of-the-variable.patch
index 19c9050..3174e2c 100644
--- a/debian/patches/0002-Override-the-assignment-of-the-variable.patch
+++ b/debian/patches/0002-Override-the-assignment-of-the-variable.patch
@@ -8,10 +8,10 @@ step
  Makefile | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)
 
-diff --git a/Makefile b/Makefile
-index a779774..65c38d6 100644
---- a/Makefile
-+++ b/Makefile
+Index: mergerfs.git/Makefile
+===================================================================
+--- mergerfs.git.orig/Makefile
++++ mergerfs.git/Makefile
 @@ -29,12 +29,13 @@ RPMBUILD  = rpmbuild
  GIT2DEBCL = ./tools/git2debcl
  PKGCONFIG = pkg-config
diff --git a/debian/patches/cross.patch b/debian/patches/cross.patch
index ef5f8bb..5dac7f2 100644
--- a/debian/patches/cross.patch
+++ b/debian/patches/cross.patch
@@ -6,9 +6,11 @@ Subject: cross
  libfuse/Makefile | 5 +++--
  1 file changed, 3 insertions(+), 2 deletions(-)
 
---- a/libfuse/Makefile
-+++ b/libfuse/Makefile
-@@ -24,6 +24,7 @@
+Index: mergerfs.git/libfuse/Makefile
+===================================================================
+--- mergerfs.git.orig/libfuse/Makefile
++++ mergerfs.git/libfuse/Makefile
+@@ -24,6 +24,7 @@ BINDIR        = $(EXEC_PREFIX)/bin
  SBINDIR       = /sbin
  MANDIR        = $(DATAROOTDIR)/man
  MAN1DIR       = $(MANDIR)/man1
@@ -16,7 +18,7 @@ Subject: cross
  
  INSTALLBINDIR  = $(DESTDIR)$(BINDIR)
  INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
-@@ -107,8 +108,8 @@
+@@ -119,8 +120,8 @@ clean:
  distclean: clean
  
  strip:
diff --git a/libfuse/ChangeLog b/libfuse/ChangeLog
deleted file mode 100644
index 8371584..0000000
--- a/libfuse/ChangeLog
+++ /dev/null
@@ -1,3547 +0,0 @@
-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.
-
-* Initilaize 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)
-==================================
-
-
-2013-06-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: fix multiple close of device fd.  Reported by Dan
-	Greenfield
-
-2013-03-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: fix thread cancel race.  Exiting a worker my race with
-	cancelling that same worker.  This caused a segmenation
-	fault. Reported and tested by Anatol Pomozov
-
-2013-02-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: fix crash in unlock_path().  Patch by Ratna Manoj
-
-	* libfuse: fix the 'remember' option.  The lru list was not
-	initialized for the "/" path.  This resulted in remove_node_lru()
-	crashing on LOOKUP-DOTDOT.  Patch by Madan Valluri
-
-	* libfuse: configure: detect new util-linux
-
-	* libfuse: Use AC_CONFIG_HEADERS instead of AM_CONFIG_HEADER.
-	Patch by Anatol Pomozov
-
-	* libfuse: rename ./configure.in to ./configure.ac.  Patch by
-	Anatol Pomozov
-
-2012-10-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.9.2
-
-2012-10-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix deadlock in libfuse.  Running "svn update" on a fuse
-	filesystem could deadlock because of a bug in the way the paths
-	are locked.  Reported by Kazuaki Anami
-
-2012-08-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix missing config.h in buffer.c.  Reported by Matthew Gabeler-Lee
-
-2012-08-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Not unhashing the name in forget (commit on 2011-12-09) broke
-	the forget logic in a subtle way, resulting in "fuse internal
-	error: node NNN not found" and causing the filesystem daemon to
-	abort.  Fix by incrementing the node refcount if nlookup goes from
-	zero to one.  Reported by Kyle Lippincott
-
-2012-08-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix linking against GNU libiconv.  Patch by Natanael Copa
-
-2012-07-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.9.1
-
-2012-07-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix crash caused by freeing a stack address.  Reported by Itay
-	Perl
-
-2012-07-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix install of mount.fuse from out-of-tree build.  Patch by
-	Olivier Blin
-
-	* Fix build with automake >= 1.12.1.  Patch by Olivier Blin
-
-2012-04-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fallocate operation.  Only works on linux kernels 3.5 or
-	later.  Patch by Anatol Pomozov
-
-2012-05-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Linking to a library that uses threads requires the application
-	to be linked with -pthreads otherwise some pthread functions will
-	be linked to stubs in glibc.  So move -pthread from Libs.private
-	to Libs in fuse.pc.  Reported by Werner Fink
-
-	* Fix the compile command in the examples. Reported by Luciano
-	Dalle Ore
-
-2012-04-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.9.0
-
-2012-04-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing fuse_fs_flock to fuse_versionscript
-
-2012-04-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check protocol version before sending notifications and return
-	-ENOSYS if a particular notification is not supported.
-
-	* Add 'flag_utime_omit_ok' flag to fuse_operations.  If the
-	filesystem sets this flag then ->utimens() will receive UTIME_OMIT
-	and UTIME_NOW values as specified in utimensat(2).
-
-2012-01-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Interpret octal escape codes in options.  Requested by Jan
-	Engelhardt
-
-2012-01-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add man pages for fusermount, mount.fuse and ulockmgr_server.
-	Lifted from the Debian package.  The man pages were written by
-	Daniel Baumann and Bastien Roucaries
-
-2012-01-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Disable symbol versions on MacOSX.  Patch by Anatol Pomozov
-
-2012-01-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove unnecessary mutex unlock at the end of multithreaded
-	event loop.
-
-2011-12-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix hang in wait_on_path().  Reported by Ville Silventoinen
-
-	* Don't unhash name in FORGET.  This resulted in ENOENT being
-	returned for unlinked but still open files if the kernel sent a
-	FORGET request for the parent directory.
-
-	* Free request in fuse_reply_data().
-
-2011-12-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix build if FUSE_NODE_SLAB is not defined.  Patch by Emmanuel
-	Dreyfus
-
-	* Check for availability of utimensat() function.  Patch by
-	Emmanuel Dreyfus
-
-2011-12-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fuse_lowlevel_notify_delete() which tells the kernel that a
-	file or directory is deleted.  Patch by John Muir
-
-2011-12-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Update retrieve_reply() method
-
-2011-12-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Low level API: lock argument of fuse_reply_lock should have a
-	'const' qualifier.  Reported by Shachar Sharon
-
-	* Add support for ioctl on directories.  Reported by Antonio SJ
-	Musumeci
-
-2011-10-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Reply to request with ENOMEM in case of failure to allocate
-	request structure.  Otherwise the task issuing the request will
-	just freeze up until the filesystem daemon is killed.  Reported by
-	Stephan Kulow
-
-2011-09-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Replace daemon() function with fork().  Patch by Anatol Pomozov
-
-2011-08-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* If configured with --disable-mtab then don't call mount(8) from
-	libfuse to update the mtab.  Reported by: James Sierp
-
-2011-08-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Use LRU list for cleaning up the cache if the "remember=T"
-	option was given.  Patch by therealneworld@gmail.com
-
-2011-07-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add ->flock() operation to low and high level interfaces.  This
-	fixes problems with emulating flock() with POSIX locking.
-	Reported by Sebastian Pipping.  As with lock/setlk/getlk most
-	filesystems don't need to implement this, as the kernel takes care
-	of file locking.  The only reason to implement locking operations
-	is for network filesystems which want file locking to work between
-	clients.
-
-2011-07-02  Sebastian Pipping <sebastian@pipping.org>
-
-	* Make xmp_utimens of examples "fusexmp" and "fusexmp_fh"
-	not follow symlinks as other layers do that already.
-
-2011-06-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add "remember" option.  This works similar to "noforget" except
-	that eventually the node will be allowed to expire from the cache.
-	Patch by therealneworld@gmail.com
-
-2011-05-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check if splice/vmsplice are supported
-
-2011-05-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove -lrt -ldl from fuse.pc for dynamic linking since
-	libfuse.so is already linked with these libraries.  Reported by:
-	Nikolaus Rath
-
-2011-05-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Cleaner build output.  Patch by Reuben Hawkins
-
-2011-05-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Disable splice by default, add "splice_read", "splice_write" and
-	"splice_move" options.  Keep the "no_splice_*" variants, which can
-	disable splice even if the filesystem explicitly enables it.
-
-2011-04-15  Max Krasnyansky <maxk@kernel.org>
-	* Added support for "auto_unmount" option which unmounts the
-	filesystem automatically on process exit (or crash).
-
-2011-03-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Patches by Laszlo Papp fixing various issues found by the
-	Coverity checker
-
-2011-03-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* In case of failure to add to /etc/mtab don't umount.  Reported
-	by Marc Deslauriers
-
-2011-02-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: In fuse_session_loop_mt() don't pause when exiting the
-	worker threads.  The pause() was added in 2.2.1 to prevent
-	segfault on pthread_cancel() on an exited, detached thread.  Now
-	worker threads are not detached and pthread_cancel() should work
-	fine even after the thread exited.  Reported by Boris Protopopov
-
-2011-01-31  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: chdir to / before performing mount/umount
-
-	* fusermount: only allow mount and umount if util-linux supports
-	--no-canonicalize
-
-2010-12-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Highlevel lib: allow hash tables to shrink
-
-	* Highlevel lib: add slab allocation for node cache.  This will
-	allow the memory used by the filesystem to grow and shrink
-	depending on how many inodes are currently cached.
-
-2010-12-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Highlevel lib: use dynamically resized hash table for looking up
-	by name and node ID.
-
-2010-12-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Allow batching of forget requests.  This allows forget requests
-	to be processed faster and doesn't require a modification to fuse
-	filesystems.  Reported by Terje Malmedal
-
-	* Add ->forget_multi() operation to the lowlevel API.  The
-	filesystem may implement this to process multiple forget requests
-	in one call
-
-	* Fix the ambiguity of ioctl ABI on the kernel/userspace boundary
-	for 32bit vs. 64bit userspace
-
-2010-11-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add new write_buf() method to the highlevel API.  Similarly to
-	the lowlevel write_buf() method, this allows implementing zero
-	copy writes.
-
-	* Add a new read_buf() method to the highlevel API.  This allows
-	returning a generic buffer from the read method, which in turn
-	allows zero copy reads.
-
-	* In fusexmp_fh implement the ->read_buf() and ->write_buf()
-	methods.  Leave the ->read() and ->write() implementations for
-	reference, even though they are not necessary.
-
-2010-11-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix check for read-only fs in mtab update
-
-	* Open /dev/null for write instead of read for redirecting stdout
-	and stderr
-
-	* If umount(8) supports --fake and --no-canonicalize (util-linux-ng
-	version 2.18 or later), and umount(2) supports the
-	UMOUNT_NOFOLLOW flag (linux kernel version 2.6.35 or later)  then,
-	"fusermount -u" will call the umount(2) system call and use
-	"umount --fake ..." to update /etc/mtab
-
-	* Added --disable-legacy-umount option to configure.  This
-	disables the runtime checking of umount(8) version.  When built
-	with this option then "fusermount -u" will fail if umount(8)
-	doesn't support the --fake and --no-canonicalize options.
-
-	* Fix fuse_buf_copy() if already at the end of the buffers
-
-	* Add new ->write_buf() method to low level interface.  This
-	allows passig a generic buffer, either containing a memory buffer
-	or a file descriptor.  This allows implementing zero copy writes.
-
-	* Add fuse_session_receive_buf() and fuse_session_process_buf()
-	which may be used in event loop implementations to replace
-	fuse_chan_recv() and fuse_session_process() respectively.
-
-	* Remove unnecessary restoring of current working directory in
-	"fusermount -u"
-
-	* Add ctx->pid to debug output
-
-	* Fix st_nlink value in high level lib if file is unlinked but
-	still open
-
-	* libfuse: add store request.  Request data to be stored in the
-	kernel buffers for a given inode.
-
-	* libfuse: add retrieve request.  Retrieve data stored in the
-	kernel buffers for a given inode.
-
-2010-10-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Use LTLIBICONV when linking libfuse.  This fixes building against
-	uclibc + libiconv.  Patch by Natanael Copa
-
-2010-10-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing argument check in ulockmgr.c to prevent calling
-	ulockmgr_server with illegal arguments. This would cause an ever
-	growing list of ulockmgr_server processes with an endless list of
-	open files which finally exceeds the open file handle limit.
-	Patch by Markus Ammer
-
-2010-09-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix ambiguous symbol version for fuse_chan_new.
-	fuse_versionscript included fuse_chan_new in both FUSE_2.4 and
-	FUSE_2.6.  Remove the FUSE_2.4, which is invalid.
-
-2010-09-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix option escaping for fusermount.  If the "fsname=" option
-	contained a comma then the option parser in fusermount was
-	confused (Novell bugzilla #641480).  Fix by escaping commas when
-	passing them over to fusermount.  Reported by Jan Engelhardt
-
-2010-08-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add NetBSD support.  Patch from Emmanuel Dreyfus
-
-2010-07-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: add buffer interface.  Add a generic buffer interface
-	for use with I/O.  Buffer vectors are supplied and each buffer in
-	the vector may be a memory pointer or a file descriptor.
-
-	* The fuse_reply_fd() interface is converted to using buffers.
-
-2010-06-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make the number of max background requests and congestion
-	threshold tunable.  New options are "max_background" and
-	"congestion_threshold".  Only effective on linux kernel versions
-	2.6.32 or greater.  Patch by Csaba Henk
-
-2010-06-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fuse_reply_fd() reply function to the low level interface.
-	On linux version 2.6.35 or greater this will use splice() to move
-	data directly from a file descriptor to the fuse device without
-	needing to go though a userspace buffer.  With the
-	FUSE_REPLY_FD_MOVE flag the kernel will attempt to move the data
-	directly into the filesystem's cache.  On earlier kernels it will
-	fall back to an intermediate buffer.  The options
-	"no_splice_write" and "no_splice_move" can be used to disable
-	splicing and moving respectively.
- 
-2010-06-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix out-of-source build.  Patch by Jörg Faschingbauer
-
-	* Add a "nopath" option and flag, indicating that path argument
-	need not be calculated for the following operations: read, write,
-	flush, release, fsync, readdir, releasedir, fsyncdir, ftruncate,
-	fgetattr, lock, ioctl and poll.
-
-2010-05-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove "chmod root" from install of fusermount.  Reported by
-	Lucas C. Villa Real
-
-2010-04-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.4
-
-2010-04-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix checking for symlinks in umount from /tmp.  Reported by Al
-	Viro
-
-	* Fix umounting if /tmp is a symlink.  Reported by Franco Broi
-
-2010-02-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix definition of FUSE_OPT_END for C++.  Reported by Tim
-	Bruylants
-
-2010-02-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix stack alignment for clone()
-
-2010-02-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.3
-
-2010-02-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Using "--no-canonicalize" with umount(8) conflicts with the race
-	fix, sinceit assumes the supplied path is absolute, while the race
-	fix relies on the path being relative to the current directory.
-	Reported by Tom Rindborg
-
-2010-01-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.2
-
-2010-01-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix race if two "fusermount -u" instances are run in parallel.
-	Reported by Dan Rosenberg
-
-	* Make sure that the path to be unmounted doesn't refer to a
-	symlink
-
-2010-01-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compile error on FreeBSD.  Patch by Jay Sullivan
-
-2009-12-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Use '--no-canonicalize' option of mount(8) (available in
-	util-linux-ng version 2.17 or greater) to avoid calling
-	readling(2) on the newly mounted filesystem before the mount
-	procedure is finished.  This has caused a deadlock if "audit" was
-	enabled in the kernel.  Also use '--no-canonicalize' for umount to
-	avoid touching the mounted filesystem.
-
-2009-09-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.1
-
-2009-08-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix missing versioned symbol fuse_get_context@FUSE_2.2
-
-2009-08-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.0
-
-2009-08-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing fuse_session_data to versionscript
-
-	* Make sure all global symbols are prefixed with "fuse_" or "cuse_"
-
-2009-07-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Clarify how the protocol version should be negotiated between
-	kernel and userspace.  Notably libfuse didn't correctly handle the
-	case when the supported major versions didn't match
-
-	* Add missing pthread link for libulockmgr.  Patch by  Petr Salinger
-
-2009-07-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* The context is extended with a 'umask' field.  The umask is sent
-	for mknod, mkdir and create requests by linux kernel version
-	2.6.31 or later, otherwise the umask is set to zero.  Also
-	introduce a new feature flag: FUSE_CAP_DONT_MASK.  If the kernel
-	supports this feature, then this flag will be set in conn->capable
-	in the ->init() method.  If the filesystem sets this flag in in
-	conn->want, then the create modes will not be masked.
-
-	* Add low level interfaces for lookup cache and attribute
-	invalidation.  This feature is available in linux kernels 2.6.31
-	or later.  Patch by John Muir
-
-	* Kernel interface version is now 7.12
-
-	* fusermount: Do not silently ignore command line arguments.
-	Patch by Sebastian Harl
-
-2009-06-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.0-pre3
-
-2009-06-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fuse_getgroups (high level lib) and fuse_req_getgroups (low
-	level lib) functions to query the supplementary group IDs for the
-	current request.  Currently this is implemented on Linux by
-	reading from the /proc filesystem.
-
-2009-06-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add "noforget" option to high level lib to prevent ESTALE errors
-	on NFS exported filesystems.  This result in paths being cached
-	forever, resulting in ever growing memory usage.  Use with care.
-
-	* Add "no_remote_lock" option to disable remote file locking even
-	if the filesystem implements it.  With this option locking
-	primitives (flock, lockf, fcntl(F_SETLK)) will still work, but
-	will ignore remotely locked files.
-
-	* CUSE patches from Tejun Heo:
-
-	* Unrestricted ioctl support left some debris.  Clean them up:
-	  o No reason to pass around pointer to flags.  Pass flags directly.
-	  o Clean up comment and prototype parameter names.
-	  o fuse_lib_ioctl() didn't reply when get_path() failed.  Fix it.
-	  o Remove unused variables {in|out}_iov from fuse_lib_ioctl().
-
-	* Add fuse_reply_ioctl_iov()
-
-	* Move fuse_session, fuse_req and fuse_ll definitions to fuse_i.h
-	and make send_reply_iov() and fuse_setup_common() global (also in
-	fuse_i.h).  These will be used by CUSE support.
-
-	* Restructure fuse_ll_process()
-
-	* Implement libfuse side of CUSE support.  CUSE uses subset of FUSE
-	operations as dir operations don't make sense for CUSE where one
-	instance implements single character device.
-
-	CUSE support comes with its own cuse_lowevel_ops and related
-	initialization and helper functions.  Except for initialization, it
-	usage is basically identical to FUSE.
-
-	This patch also adds example/cusexmp.c which can create a character
-	device with name and device number specified on command line.  The
-	created device itself is pretty boring.  It's a bit bucket supporting
-	read, write and access via ioctl.
-
-2009-06-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing fuse_reply_bmap to versionscript.  Debian
-	Bug#531329.  Reported by Goswin Brederlow
-
-2009-05-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't call forget_node() if the lookup was negative and write()
-	for the reply returned ENOENT.  Reported by John Haxby
-
-2009-05-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add FUSE_CAP_EXPORT_SUPPORT to fuse_common.h
-
-2009-05-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix missing newlines in some printfs
-
-	* Fix 'make install-strip'.  Reported by Dominick Layfield
-
-2009-01-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.0-pre2
-
-2008-12-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Implement poll support.  Patch by Tejun Heo
-
-	* Add missing setattr flags to <fuse_lowlevel.h>.
-
-	* Only pass valid flags to ->setattr().
-
-2008-12-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Implement ioctl support.  On high level interface only
-	"restricted" ioctls are supported (which are defined with the
-	_IO(), _IOR(), _IOW() or _IOWR() macros).  Unrestricted ioctls
-	will only be allwed to CUSE (Character Device in Userspace)
-	servers.  Patch by Tejun Heo
-
-2008-11-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* If open sets fi->nonseekable, libfuse will tell the kernel that
-	the file is not seekable.  Patch by Tejun Heo
-
-2008-11-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lowlevel lib: fix deadlock if fuse_reply_* is called from the
-	interrupt handling function.  Reported by Tero Marttila
-
-2008-10-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Allow commas in options to be escaped with a backslash
-
-	* Add new function: fuse_opt_add_opt_escaped()
-
-	* Add missing fuse_reply_bmap() to the version script
-
-2008-10-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Pass current file flags to read and write operations
-
-2008-07-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Clean up debug output in highlevel lib
-
-2008-07-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.8.0-pre1
-
-2008-06-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix handling of (no)suid and (no)dev options if filesystem is
-	mounted from /etc/fstab or via mount(8).  Reported by Jan Ondrej.
-
-	* Skip calling mount(8) if /etc/mtab doesn't exist or if it's on a
-	read-only filesystem.  This works around issues with certain mount
-	implementations.  Reported by Szabolcs Szakacsits.
-
-2008-06-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove fuse kernel module sources.  Linux 2.6.27 will support
-	NFS exporting.
-
-2008-06-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix theoretical infinite loops in libfuse.  Reported by Szabolcs
-	Szakacsits
-
-	* Fix missing <sys/param.h> include for PATH_MAX.  Reported by
-	Szabolcs Szakacsits
-
-2008-05-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix mounting over symlink.  Reported by Szabolcs Szakacsits
-
-2008-05-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't allow bigger than 4kB writes by default on 2.6.26 and
-	later kernels, so that filesystems not expecting this are not
-	broken on a kernel upgrade.  Provide a 'big_writes' mount option
-	to enable this feature.  In future API revisions this may become
-	the default.
-
-2008-04-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Update warning message for missing newline at end of fuse.conf
-
-	* Update debug message for successful operation to not include the
-	string "error:"
-
-2008-04-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Update error message for missing mountpoint parameter.  Reported
-	by Allen Pulsifer
-
-2008-04-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Print library version information to debug output
-
-	* Highlevel lib: don't limit paths to 4095 characters
-
-2008-03-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix memory leaks on mount.  Patch by Szabolcs Szakacsits
-
-2008-03-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix missing pthread_mutex_destroy in error path of
-	fuse_lib_opendir().  Patch by Szabolcs Szakacsits
-
-2008-03-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add queuing on contention to per-node lock algorithm, to avoid
-	starvation.
-
-	* Only enable cancelation when reading a request, otherwise
-	cancellation could happen with a mutex held, which could hang the
-	process on umount
-
-2008-02-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Block SIGCHLD when executing mount and umount
-
-	* fusexmp_fh: avoid unnecessary seeking in readdir
-
-	* Update kernel interface to 7.9:
-
-	* Support receiving file handle from kernel in GETATTR request
-
-	* Allow operations with a NULL path argument, if the filesystem
-	supports it
-
-	* Add support atomic open(O_TRUNC)
-
-	* Support the st_blksize field in struct stat
-
-	* If the "FUSE_THREAD_STACK" environment is set, initialize the
-	stack size of threads by this value.  Patch by Florin Malita
-
-	* Add per-node locking, instead of a global tree lock to protect
-	the path from changing during operations.  Original patch by
-	Rodrigo Castro
-
-2008-02-03  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib/mount_bsd.c:
-	- string formatting fixes
-	- exit if mounting has failed
-	  (in FreeBSD a mount failure is not critical per se, as the daemon
-	  still could be mounted externally, but waiting for such an event
-	  is more confusing than fruitful)
-	- ditch the kvm(8) stuff and simply use forced unmount which just
-	  won't block
-	- prettify option specifications
-	- add "-onosync_unmount" kernel option
-
-2008-01-07  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib/mount_bsd.c:
-	- refine device closing in a race-free way
-	- add support for "-osubtype" on FreeBSD
-
-	* makeconf.sh: make it work under FreeBSD
-
-2008-01-03  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib/mount_bsd.c: close device before unmount
-	(cf. lib/mount.c rev. 1.43) and fix some warnings 
-
-2007-12-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix './configure --disable-static'.  Patch from Ismail Dönmez
-
-2007-12-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.7.2
-
-2007-12-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix kernel module compile for 2.6.24
-
-	* Invalidate attributes of parent directory after create(), since
-	the modification time changes.  Invalidate attributes on rename,
-	since some filesystems may update st_ctime.  Reported by Szabolcs
-	Szakacsits
-
-	* Fix NFS exporting to handle 64bit node IDs
-
-	* Disable old symbol versions if __UCLIBC__ is defined.  If a
-	symbol in a library has multiple versions, the runtime linker in
-	uClibc seems to randomly choose between them.
-
-	* Remove erroneous 'fuse_opt_insert_arg@FUSE_2_5' from
-	fuse_version_script.  fuse_opt_free_args() was added in fuse-2.6.
-
-	* Close fuse device file descriptor before calling umount(),
-	preventing a deadlock when umount is synchronous.  Reported by
-	Szabolcs Szakacsits
-
-2007-11-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* 'fusermount -u' did not umount the filesystem if /etc/mtab was a
-	symlink.  This bug was introduced in 2.7.1 by "Don't call
-	/bin/[u]mount if /etc/mtab is a symlink".  Found by robertsong.
-
-2007-10-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.7.1
-
-2007-10-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Clarify licence version to be "LGPLv2" for the library
-
-	* kernel fixes:
-
-	* After mount set nlink attribute for the root inode to 1
-
-	* Fix wake up of task waiting for a reserved request
-
-	* Fix allowing setattr, listxattr and statfs for other users
-
-2007-09-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing context initialization in fuse_fs_chmod().  Bug
-	found by "iohead"
-
-	* Fix kernel module compilation for 2.6.23.  Based on patch by
-	Marian Marinov
-
-2007-09-04  Philippe Elie  <phil.el@wanadoo.fr>
-
-	* lib/fuse_lowlevel.c: fix a fuse_req leak in do_forget()
-
-2007-07-31  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Work around hotplug issue, that it calls filesystem with file
-	descriptors 0, 1 and 2 not open.  Tracked down by Leif Johnson
-
-2007-07-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't call /bin/[u]mount if /etc/mtab is a symlink.  Reported by
-	Tomas M
-
-	* Also don't touch /etc/mtab if it is within the mounted
-	filesystem.  Suggested by Jeffrey Law
-
-2007-07-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Reset args->argc in fuse_opt_free_args().  Patch by Lucas
-	C. Villa Real
-
-2007-07-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.7.0
-
-2007-07-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Accept a NULL "op" for fuse_main(), etc.  This is useful if
-	filesystem is only invoking fuse to print a help message, or
-	version.  Fixes RedHat bugzilla #217343
-
-2007-06-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: fix locking when loading a filesystem module
-
-2007-06-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fs subtype support to mount.fuse
-
-2007-06-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fs subtype support to libfuse and fusermount
-
-2007-06-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: sync with mainline (2.6.22)
-
-2007-06-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Send debug output to stderr instead of stdout.  Patch by Jan
-	Engelhardt
-
-2007-06-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libulockmgr: Work around a kernel bug in recv(), causing it to
-	sometimes return zero even if data was available on the socket.
-
-2007-05-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: optimization: store parent pointer in node instead of
-	parent id
-
-2007-05-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: don't create new thread for each FORGET request.  FORGET
-	messages sometimes caused so many threads to be created, that
-	process virtual memory space ran out.  Reported by Chris AtLee
-
-2007-05-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: fix memory leak on thread creation failure in multithreaded
-	event loop.  Found by Chris AtLee
-
-2007-05-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lowlevel lib: add fuse_reply_iov function, which is similar to
-	fuse_reply_buf, but accepts a vector of buffers.  Patch by Roger
-	Willcocks
-
-2007-05-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix Oops or error if a regular file is created with mknod(2) on
-	a fuse filesystem.  Kernels 2.6.18 onward are affected.  Thanks to
-	J. Cameijo Cerdeira for the report
-
-2007-05-11  Csaba Henk <csaba.henk@creo.hu>
-
-	* libfuse: fix return value of fuse_loop()/fuse_loop_mt().
-	Error reported by Csaba Henk, fix by Miklos Szeredi
-
-	* libfuse: fix unlock in flush
-
-	* libfuse: do unlocking on RELEASE+FLUSH
- 
-2007-05-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.7.0-rc1
-
-2007-05-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: sync with mainline:
-
-	* Use invalidate_mapping_pages() if available
-
-	* Fix BUG when invalid file type is supplied in mount. Patch by
-	Timo Savola
-
-2007-04-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: call umount(8) directly instead of fusermount if
-	possible
-
-	* Clean up init script, make it LSB compliant
-
-2007-04-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* In multithreaded loop, use a semaphore instead of SIGHUP to wake
-	up the main thread on umount.  This is more elegant, and works
-	even if signals are blocked.
-
-2007-04-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Improve mounting support in libfuse:
-	 - check non-empty mountpoint
-	 - only fall back to fusermount when necessary
-
-2007-04-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't chdir to "/" in foreground mode, it causes more trouble
-	than it's worth
-
-2007-04-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Replace utils/mount.fuse "sh" script with a "C" program
-
-2007-04-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add -lulockmgr to compilation comment in fusexmp_fh.c
-
-2007-04-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check for iconv.  Patch by Csaba Henk
-
-	* Add direct umounting
-
-	* Use "fusectl" as the device for the fusectl filesystem.  Debian
-	Bug#417945.  Reported by Laurent Bonnaud
-
-2007-04-01  Csaba Henk <csaba.henk@creo.hu>
-
-	* Fix some FreeBSD related macros.
-
-2007-03-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add support for direct mounting by libfuse.  Fall back on
-	calling fusermount if it doesn't work
-
-2007-03-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.7.0-pre1
-
-2007-03-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Correctly handle O_APPEND in direct IO mode.  Reported by Greg
-	Bruno
-
-	* mount.fuse should use /bin/bash.  Debian Bug#413403.  Reported
-	by Thomas Weinbrenner
-
-2007-02-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix detection of installed fuse in init script.  Reported and
-	fix suggested by Davide Canova
-
-2007-02-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix 2.6.9 RHEL kernels, which have compatibility mutex.h, but
-	don't define mutex_destroy(), bummer.  Patch from Phil Schwan
-
-2007-02-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Compile fuseblk for kernels which don't have an option to turn
-	off the block layer (CONFIG_BLOCK).  Reported by Szakacsits
-	Szabolcs
-
-2007-02-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add filesystem stacking support to high level API.  Filesystem
-	modules can be built into libfuse or loaded from shared object
-	(.so) files
-
-	* Add 'subdir' and 'iconv' built in modules
-
-	* lib/fuse.c: Fix locking for the reply code in create and open
-
-2007-02-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: make it compile on "strange" kernels which have emulated
-	mutexes via <linux/mutex.h> but no i_mutex.  Reported by Tomasz
-	Mateja
-
-2007-01-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix BUG in control filesystem if it is umounted and
-	mounted again, while some fuse filesystems are present.
-	Bugreport from Florent Mertens
-
-	* kernel: sync with mainline, support 2.6.20
-
-2007-01-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib/Makefile.am: actually link libfuse against libfuse_libs
-
-2007-01-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Build fix for 2.6.16 vanila and 2.6.15 FC5 kernels.  Patch from
-	Ian Abbott
-
-2007-01-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix abort in fuse_new() compatibility API for opts == NULL case.
-	Novell bugzilla #233870.  Patch from Takashi Iwai.
-
-2007-01-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix option parsing in mount.fuse.  Patch from Jens M. Noedler
-
-2007-01-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix unaligned access in file desctriptor passing in libfuse,
-	fusermount and ulockmgr.  Debian bug ID: 404904.  Reported and
-	tested by Sebastian Fontius
-
-2006-12-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: don't keep unreferenced inodes in the icache.
-
-2006-12-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: Fix detection of fuseblk.  Reported by Szakacsits
-	Szabolcs
-
-	* lib: Fix use after free in fuse_flush().  Reported by Ron
-	Lindman
-
-2006-12-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* mount.fuse: add "setuid=USER" option which does a "su - USER"
-	for the filesystem
-
-	* fusermount: use "/bin/mount -f" to add entry to /etc/mtab, and
-	"/bin/umount" to remove entry from /etc/mtab.  This gets rid of
-	the ugly code dealing with mtab, as well as a possible race
-	between fusermount and mount trying to modify /etc/mtab at the
-	same time
-
-	* Fix "buffer size too small: 4" warning for users of the
-	fuse_loop_mt_proc() function.
-
-2006-12-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix warnings with gcc-4.1 on 64bit archs.  Report from
-	Harshavardhana
-
-	* Add extra warning options, and fix resulting warnings
-
-	* Really fix fuse_teardown problem
-
-2006-12-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add -lrt to fuse.pc (if needed) to fix static linking against
-	libfuse.  Reported by Szakacsits Szabolcs
-
-2006-12-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.1
-
-2006-11-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix API version 21 and 22 compatibility for fuse_teardown.
-	Reported by Bgs
-
-2006-11-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: Print a more helpful message in case the kernel
-	doesn't support the 'fuseblk' filesystem type.  This has been
-	biting ntfs-3g users.  Reported by Yura Pakhuchiy
-
-	* kernel: fix build problem for "make -C ...".  Reported by
-	Stephen Bryant
-
-2006-11-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug in certain error paths of lookup routines.  The request
-	object was reused for sending FORGET, which is illegal.  This bug
-	could cause an Oops in linux-2.6.18 or in fuse-2.6.0, and might
-	silently corrupt memory in earlier versions.  Report and test
-	program by Russ Cox
-
-2006-11-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Print an error if an incompatible kernel interface version is
-	detected in INIT.  This will only show if filesystem is started
-	with -d or -f
-
-	* Fix order of fuse_destroy()/fuse_unmount() in error cleanup of
-	fuse_setup_common().  Reported by Szakacsits Szabolcs
-
-2006-11-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix recursive locking in fuse_create().  Thanks to Takuya
-	Ishibashi for the bug report
-
-2006-10-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix automake problem.  Patch from Nix
-
-2006-10-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix mount.fuse to use /bin/sh instead of /bin/bash, which is not
-	always available on embedded systems.  Patch from Paul Smith
-
-	* Fix util/Makefile.am, so that failure to run update-rc.d or
-	device creation doesn't cause make to fail.  Reported by Paul
-	Smith
-
-2006-10-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0
-
-2006-10-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: don't try to create a lock file if /etc/mtab is a
-	symlink.  Report and patch from Alexei Sheplyakov (debian bug
-	#393693)
-
-2006-10-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Minor changes, sync with mainline tree
-
-2006-10-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0-rc3
-
-2006-10-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: cleanups
-
-2006-10-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: Fix compilation on patched 2.6.18 (fc6) and 2.6.19.
-	Report from David Shaw
-
-	* lib: Fix lost error on renaming a file. Report from David Shaw
-
-	* lib: Fix lost error on hiding open files (renaming to
-	.fuse_hiddenXXXX)
-
-	* kernel: Fix a rare hang on SMP/32bit on heavy filesystem
-	activity.  The cause of the bug was that some calls to
-	i_size_write() were not protected by a lock, and hence
-	i_size_seqcount could become corrupted.  This caused subsequent
-	calls to i_size_read() to spin forever.  This is a long standing
-	bug was probably introduced in version 2.2, and thought to be
-	related to NFS exporting (it's not).  It was reported by various
-	people, but Dana Henriksen has finally helped me to track it down,
-	so big thanks to him
-
-	* kernel: Protect against truncation of a swapfile
-
-2006-10-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: Check for signature of super_operations->umount_begin().
-	Ubuntu kernel 2.6.17 seems to use the new signature found in
-	2.6.18.  Thanks to Florent Mertens for the report
-
-2006-10-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make sure inode numers wrap around at 2^32.  This is needed on
-	dual 64bit/32bit architectures, because 32bit applications using
-	the non-largefile interface would otherwise break (EOVERFLOW error
-	would be returned by the stat() system call family)
-
-	* ulockmgr: handle the case, when a locking operation fails
-	because no more file desctriptors are available in
-	ulockmgr_server.  Also work around a Linux kernel bug (known to
-	exist for all Linux kernel versions <= 2.6.18) which may cause
-	sent file descriptors to be lost in the above case
-
-	* ulockmgr: optimize file descriptor use
-
-	* restore needed cpp flags to util/Makefile.am
-
-	* Install udev rules as 99-fuse.rules instead of 60-fuse.rules
-
-	* Minor clean up of udev rules
-
-	* Add a synchronous DESTROY message to kernel interface.  This is
-	invoked from umount, when the final instance of the filesystem is
-	released.  It is only sent for filesystems mounted with the
-	'blkdev' option for security reasons.
-
-	* If the DESTROY message is received, call the filesystem's
-	->destroy() method.  In this case it's not called from session
-	destruction as it would be otherwise.
-
-2006-10-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0-rc2
-
-2006-10-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add support for FLUSH+RELEASE operation for FreeBSD.  Original
-	patch by Csaba Henk
-
-	* Add init script to insert fuse module and mount the control
-	filesystem.  The script is installed as /etc/init.d/fuse and on
-	debian based systems (where update-rc.d is available) symlinks
-	from /etc/rc*.d/ are also installed.
-
-	* Include '#define FUSE_USE_VERSION=XX' into examples so they
-	become more self contained.
-
-2006-09-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* API changes:
-
-	* Move lock_owner from a separate argument into fuse_file_info
-
-	* Add a flag to fuse_file_info indicating (1) a highlevel lock
-	operation (unlock all) was initiated by a flush, (2) a lowlevel
-	release operation should perform a flush as well.
-
-	* fusermount: revert modprobe change (2006-08-18) since it
-	doesn't work reliably with udev
-
-	* Add support for block device backed filesystems.  This mode is
-	selected with the 'blkdev' option, which is privileged.
-
-	* Add support for the bmap (FIBMAP ioctl) operation on block
-	device backed filesystems.  This allows swapon and lilo to work on
-	such filesystems.
-
-	* kernel changes:
-
-	* Drop support for kernels earlier than 2.6.9.  Kernel module from
-	previous (2.5.x) release can be used with library from this
-	release
-
-	* In fuse_dentry_revalidate() use dget_parent() instead of
-	dereferencing d_parent, since there's no protection against parent
-	changing and going away
-
-	* Protect nlookup from concurrent updates
-
-	* In lookup if a directory alias exists but is unused,
-	then get rid of it, otherwise return -EBUSY.
-
-	* In mkdir if a directory alias exists, return success, but leave
-	dentry negative.  In reality this could happen if a remote rename
-	immediately followed the mkdir.
-
-	* Don't BUG in fuse_iget() if multiple retries are needed to get a
-	good inode.  This could happen if several lookups are racing for
-	the same inode.
-
-2006-09-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compilation on 2.6.9.  Report from Troy Ayers
-
-2006-09-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix Oops in fuse_readpages().  Reported by David Shaw
-
-2006-09-24  Csaba Henk <csaba.henk@creo.hu>
-
-	* Add support for nanosec times on FreeBSD
-
-	* Fix FreeBSD compatibility issues
-
-2006-09-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix one more compatibility bug.  Thanks to Ricardo Correia
-
-	* Fix utimens compilation with uClibc.  Patch from Jamie Guinan
-
-2006-09-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fixed several compatibility bugs in low level interface.
-	Reported by Ricardo Correia
-
-	* Add workaround for ARM caching bug
-
-2006-09-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Rename new utimes() method to more logical utimens()
-
-2006-09-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fuse tried to unlink already unlinked hidden files.  Bug
-	reported by Milan Svoboda
-
-2006-09-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0-rc1
-
-2006-09-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: Fix unlock on close for kernels < 2.6.18
-
-	* Add ulockmgr library & server.  This can be used for handling
-	file locking requests either directly from libfuse or over a
-	network, etc.  This first version is not optimized and the number
-	of file descriptors it uses may get out of hand
-
-2006-09-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: Add interrupt support to high level library, which may be
-	enabled with the 'intr' mount option.
-
-	* When an operation is interrupted the thread handling that
-	operation will receive SIGUSR1 (or other signal specified with the
-	'intr_signal=N' option).  The library installs a no-op signal
-	handler for this signal, unless there's already a handler
-	installed.
-
-	* The filesystem may query interrupt status (regardless of 'intr')
-	with the fuse_interrupted() function.
-
-	* mount.fuse: initialize $HOME if not set.  Report from Sven Goldt
-
-2006-09-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: Multithreaded loop now allows unlimited number of threads.
-	This is needed for locking operations which may block
-	indefinitely.  Also the kernel now doesn't limit the number of
-	outstanding requests so the library shouldn't do so either.
-
-2006-09-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix recursive lock bug in interrupt handling
-
-	* Add utimes() method to highlevel interface, which supports
-	setting times with nanosecond resolution
-
-2006-08-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix page leak if fuse_readpages() failed in it's
-	initialization.  Bug found and original patch from Alexander
-	Zarochentsev
-
-	* For linux kernels >=2.6.18 (2.6.19 if using the fuse module from
-	the kernel tree) the statfs method will receive the path within
-	the filesystem on which the stat(v)fs syscall was called
-
-	* fusermount: try to modprobe fuse module if invoked by root and
-	unable to open device.  This is needed with udev, since the device
-	node will be created only when the module is inserted, hence
-	module autoloading won't work.  Reported by Szakacsits Szabolcs
-
-2006-07-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: if selinux is active, restore the original file's
-	security context in unmount_rename().  Redhat bugzilla id 188561.
-	Patch from Yves Perrenoud
-
-	* Add POSIX file locking operation to high level library
-
-	* Initialize context for unlink of hidden files on umount.  Bug
-	reported by Tim Stoakes
-
-2006-07-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Multiple release() calls can race with each other, resulting in
-	the hidden file being deleted before the last release finishes.
-	Bug found and patch tested by Mark Huijgen
-
-2006-07-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: if /dev/fuse doesn't exist, suggest modprobing fuse;
-	this makes sense on systems using udev.  Reported by Szakacsits
-	Szabolcs
-
-2006-06-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0-pre3
-
-2006-06-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Support in kernel module for file locking and interruption.  The
-	same functionality is available in official kernels >= 2.6.18
-
-2006-06-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add POSIX file locking support
-
-	* Add request interruption
-
-2006-06-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing pthread_rwlock_destroy().  Patch from Remy Blank
-
-2006-06-05  Remy Blank <remy.blank@pobox.com>
-
-	* lib: canonicalize mount point in fuse_helper_opt_proc() so that
-	unmounting succeeds even if mount point was relative.
-
-2006-06-04  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib: fix emergency umount in helper.c when malloc fails.
-	(The way it was done would end up in a segfault.)
-
-2006-06-01  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib: adjust threading related compiler flags.
-	Switch to "-pthread" from "-lpthread" as that's the preferred
-	one on several platforms. Consulted with Terrence Cole and
-	Miklos Szeredi
-
-2006-05-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: search fusermount in installation directory (bindir) as
-	well as in PATH.
-
-2006-05-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: fix compilation if CLOCK_MONOTONIC is not defined.
-	Reported by Christian Magnusson
-
-2006-04-23  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib: make FreeBSD mount routine recognize if kernel features
-        backgrounded init and if it does, run the mount util in foreground
-        (similarly to Linux)
-
-2006-04-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix fput deadlock fix, the lockless solution could lead
-	to "VFS: busy inodes after umount..."
-
-	* kernel: fix race between checking and setting file->private_data
-	for the device.  Found by Al Viro
-
-2006-04-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: remove request pool, instead allocate requests on
-	demand.  Account the number of background requests, and if they go
-	over a limit, block the allocation of new requests.
-
-	* kernel: fix deadlock if backgrounded request holds the last
-	reference to the super block
-
-	* kernel: don't use fuse_reset_request() during direct I/O
-
-2006-04-06  Csaba Henk <csaba.henk@creo.hu>
-
-	* lib: Let FreeBSD mount option parsing routine recognize "no"
-	prefixes for FUSE specific options as well
-
-2006-04-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: Add missing rwlock initialization.  Patch by Ryan Bradetich
-
-2006-03-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* API changes:
-
-	* fuse_main(), fuse_setup() and fuse_new() have an additionl
-	user_data parameter
-
-	* fuse_mount() returns a 'struct fuse_chan' pointer instead of a
-	file descriptor
-
-	* fuse_unmount() receives a 'struct fuse_chan' pointer.  It
-	destroys the given channel
-
-	* fuse_teardown() no longer has a file descriptor parameter
-
-	* new exported functions: fuse_session_remove_chan(),
-	fuse_get_session(), fuse_daemonize()
-
-	* fuse_chan_recv() may now return a new channel which will be used
-	to send the reply
-
-2006-03-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0-pre2
-
-2006-03-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't unmount if already unmounted.  This fixes a problem seen
-	in the following situation: Lazy unmount a busy filesystem; Mount
-	a new one in top; When the first finally unmounts, the second also
-	unmounts.  Reported by Franco Broi
-
-2006-03-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lowlevel lib: use indirect function calls instead of a
-	switch/case construct.  Besides increased efficiency it helps
-	maintainability & readability too.  Patch from Florin Malita
-
-2006-03-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: replace global spinlock with a per-connection spinlock
-
-2006-03-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix source compatibility breakage for fuse_unmount().  Report
-	from Yura Pakhuchiy
-
-2006-03-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix O_ASYNC handling in fuse_dev_release().  From Jeff Dike
-
-2006-03-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add O_ASYNC and O_NONBLOCK support to FUSE device.  Patch by
-	Jeff Dike
-
-	* Renamed fuse_chan_receive() to fuse_chan_recv() and changed
-	interface to return -errno in case of error.
-
-2006-03-01  Csaba Henk <csaba.henk@creo.hu>
-
-	* libfuse: pass device file descriptor to fuse_unmount(), rewrite
-	FreeBSD implementation so that it uses libc (sysctl backed) instead
-	of an embdedded script (kmem backed). Adjust the control flow of
-	hello_ll so that device doesn't get closed before unmount attempt.
-
-2006-02-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Lowlevel lib: return all-zero statvfs data if filesystem doesn't
-	implement method.  This is needed on FreeBSD, and nicer on Linux
-	too.  Highlevel lib already did this.  Reported by Csaba Henk
-
-	* Fix negative entry handling.  There was a bug, that negative
-	lookups with timeouts (nodeid == 0) returned -EIO.
-
-2006-02-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix race between RELEASE and UNLINK, which might leave
-	.fuse_hidden* files around
-
-2006-02-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusexmp_fh: implement flush() method and call close() on the
-	open file descriptor.  This is needed if used on an NFS
-	filesystem, which buffers data until file is closed.  Franco Broi
-	spotted the situation when 'cp -p' failed to set the modification
-	time because of this.
-
-2006-02-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.6.0-pre1
-
-2006-02-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: fix use-after-free bug in interruptred reply_entry().
-	Patch from John Muir
-
-	* libfuse: fix wrong symbol versioning for fuse_mount.  Debian bug
-	ID: 352631.  Found by Stéphane Rosi
-
-2006-02-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Lowlevel lib: Unify fuse_dirent_size() and fuse_add_dirent()
-	into a single function fuse_add_direntry().  This cleans up the
-	interface and makes it possible to do stacking.
-
-2006-02-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix rare race betweeen abort and release caused by failed iget()
-	in fuse_create_open().
-
-	* Add 'ac_attr_timeout' option e.g. for filesystems which do their
-	own attribute caching.
-
-2006-02-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Work around FreeBSD runtime linker "feature" which binds an old
-	version of a symbol to internal references if the symbol has more
-	than one version.  This resulted in infinite recursion in
-	fuse_lowlevel_new_compat25().
-
-2006-02-10  Csaba Henk <csaba.henk@creo.hu>
-
-	* Refine clock_gettime() querying so that linker options
-	shall be set as it's appropriate for the target platform.
-
-2006-02-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix udev rule syntax.  Reported by Nix
-
-2006-02-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* In some cases udev rule seems to be ineffective when installed
-	as 40-fuse.rules but work as 60-fuse.rules.  Reported by John Hunt
-
-2006-02-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compilation when build directory is different from source
-	directory.  Reported by Frédéric L. W. Meunier
-
-2006-02-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix even bigger bug introduced in fix for request_end() on
-	2006-01-14.  Reported by Gal Rosen
-
-2006-01-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* highlevel-lib: add 'auto_cache' option.  This caches file data
-	based on modification time and size
-
-2006-01-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Sanitize storage type and help message in mount_bsd.c.  Patch
-	from Csaba Henk
-
-	* fuse_opt: add new helper constants FUSE_OPT_KEY_KEEP and
-	FUSE_OPT_KEY_DISCARD
-
-	* Add options 'max_readahead', 'sync_read' and 'async_read'
-
-	* Kernel ABI version 7.6:
-
-	* Negotiate the 'max_readahead' value and 'async_read' flags with
-	userspace in the INIT method
-
-	* Add connection info to ->init() methods to both lowlevel and
-	highlevel API
-
-	* Fall back to synchronous read() behavior if either library or
-	userspace filesystem is using the old interface version.  This is
-	needed so non-updated filesystems won't be confused by the
-	different read() behavior
-
-2006-01-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: if "fsname=" option was given, pass it to fusermount
-
-	* fuse_opt: add new fuse_opt_insert_arg() function, which is
-	needed by filesystems to implement some argument manipulations
-	correctly
-
-	* fuse_opt: fix memory leak in handling "--" option
-
-2006-01-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix detection of case when fuse is not configured into
-	the kernel either as module or built-in
-
-	* fuse_opt.h: fix incompatibility with C++ compilers by renaming
-	'template' structure member to 'templ'.  Reported by Takashi Iwai
-
-	* fuse.h: fix compatibility bugs.  Patch by Yura Pakhuchiy
-
-	* kernel: support version 2.6.16 (i_sem -> i_mutex)
-
-2006-01-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added (again) asynchronous readpages support
-
-	* Each connection now shows up under /sys/fs/fuse/connections
-
-	* Connection attributes exported to sysfs: 'waiting' number of
-	waiting requests; 'abort' abort the connection
-
-	* Connection may be aborted through either the sysfs interface or
-	with 'umount -f mountpoint'
-
-2006-01-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.5.0
-
-2006-01-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix a couple of bugs
-
-	* Order of request_end() and fuse_copy_finish() was wrong.
-	Posthumous note: Franco Broi managed to exploit this, though it
-	seemed quite impossible
-
-	* request_end() used request pointer after decrementing refcount
-
-	* Clearing ->connected or ->mounted connection flags could race
-	with setting other bitfields not protected with a lock
-
-2006-01-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: add necessary compile flags for 2.4.X/x86_64.
-	Report from Sean Ziegeler
-
-2006-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.5.0-pre2
-
-2006-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Applied patch from Csaba Henk, to update mount_bsd to new
-	fuse_mount() semantics
-
-	* Ignore auto,noauto,... options in mount.fuse.  Reported by Frank
-	Steiner and Don Taber
-
-	* fusermount: add 'dirsync' mount option
-
-2006-01-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Improved help reporting and added version reporting to library
-
-2006-01-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Change working directory to "/" even if running in the
-	foreground.  Patch from Jonathan Brandmeyer
-
-	* Changed lots of functions to use 'struct fuse_args' instead of
-	separate argc and argv
-
-	* Added fuse_parse_cmdline(), fuse_set_signal_handlers() and
-	fuse_remove_signal_handlers() functions, so that it's now pretty
-	easy to get all the functionality of fuse_main() with a filesystem
-	using the lowlevel API.
-
-2006-01-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* mount.fuse: the 'user' option should be ignored. Report and
-	solution from Mattd.
-
-	* mount.fuse: export PATH in the right place. Report and patch
-	from Hannes Schweizer
-
-2005-12-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Clean up the option parsing interface slightly, by creating an
-	"argument list" structure, that contains the argument vector and
-	count
-
-2005-12-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: check if /mnt/mtab is a symlink and don't modify it
-	in that case
-
-	* kernel: simplify request size limiting. INIT only contains
-	maximum write size, maximum path component size remains fixed at
-	1024 bytes, and maximum xattr size depends on read buffer.
-
-2005-12-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix readdir() failure on x86_64, of 32bit programs compiled
-	without largefile support.  Bug report and help from Anthony
-	Kolasny
-
-	* If lookup returns invalid mode, return -EIO instead of creating
-	a regular file
-
-	* Add current output argument vector to option processing
-	function
-
-2005-12-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix stale code in ifdef FreeBSD.  Patch from Csaba Henk
-
-2005-12-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.5.0-pre1
-
-2005-12-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: added option parsing interface, defined in
-	<fuse_opt.h>.
-
-2005-12-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Return EIO for file operations (read, write, fsync, flush) on
-	open files whose inode has become "bad".  Inodes will be marked
-	"bad" if their type changes.  Bug report by Csaba Henk
-
-2005-12-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Use bigger request buffer size.  write() did not work on archs
-	with > 4k page size, Bug report by Mark Haney
-
-	* ABI version 7.5:
-
-	* Extend INIT reply with data size limits
-
-2005-12-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix memory leak in fuse_read_cmd()/fuse_process_cmd().  Bug
-	reported by Vincenzo Ciancia
-
-	* Handle exit-by-umount in fuse_read_cmd()
-
-2005-11-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check if '-msoft-float' option is supported by compiler when
-	configuring for a 2.4.x kernel.  Bug report by Mark Haney
-
-	* In multithreaded loop send a TERM signal to the main thread if
-	one of the other threads exit.  Needed on FreeBSD for a clean exit
-	on umount.  Should not cause any harm on Linux either
-
-2005-11-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug in 32-bit file handle compatibility
-
-2005-11-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Block TERM, INT, HUP and QUIT signals in all but the main
-	thread.  According to POSIX it's not specified which thread will
-	receive these signals.
-
-	* Kernel changes:
-
-	* Check for directory aliasing on mkdir, not just on lookup
-
-	* Check for special node ID values in create+open operation
-
-	* Sync with -mm: readv, writev, aio_read and aio_write methods
-	added to file operations
-
-	* Cleanups: lookup code, page offset calculation
-
-	* ABI stepped to 7.4, changes:
-
-	* frsize member added to fuse_kstatfs structure
-
-	* added support for negative entry caching: on lowlevel API if
-	fuse_entry_param::ino is set to zero in reply to a lookup request,
-	the kernel will cache the dentry for the specified amount of time.
-
-	* libfuse: added 'negative_timeout' option: specifies how much
-	negative entries should be cached.  Default is zero, to be
-	compatible with prior versions
-
-2005-11-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add detection of mainline FUSE code in running kernel
-
-2005-11-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't use async cancelation in multithreaded loop.  This makes
-	it more portable to systems where read() is not async cancel safe.
-	Report from Andriy Gapon
-
-2005-11-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Warn if API version 11 compatibility is requested
-
-2005-11-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* More FreeBSD merge
-
-	* fusermount: don't allow mountpoints with '\n', '\t', or '\\' in
-	them, because it corrupts /etc/mtab.  Found by Thomas Biege
-	CVE-2005-3531
-
-	* libfuse: don't use system() to invoke 'fusermount -u ...'
-	because it breaks mountpoints with spaces in them into multiple
-	arguments
-
-2005-11-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Merge library part of FreeBSD port.  Patch by Csaba Henk
-
-2005-11-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Use 64bit type for file handle, so the full range supported by
-	the kernel interface is available to applications
-
-2005-11-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Moved mountpoint argument checking from fuse_parse_cmdline() to
-	fuse_mount() in preparation to FreeBSD merge.
-
-2005-11-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove unneeded close() from fuse_teardown().  Spotted by Csaba
-	Henk.
-
-2005-11-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make the statfs change backwards compatible.
-
-2005-11-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Change ->statfs() method to use 'struct statvfs' instead of
-	'struct statfs'.  This makes the API more portable since statvfs()
-	is defined by POSIX.
-
-2005-10-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fgetattr() method, which currently will only be called after
-	a successful call to a create() method.
-
-2005-10-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Change kernel ABI version to 7.3
-
-	* Add ACCESS operation.  This is called from the access() system
-	call if 'default_permissions' mount option is not given, and is
-	not called on kernels 2.4.*
-
-	* Add atomic CREATE+OPEN operation.  This will only work with
-	2.6.15 (presumably) or later Linux kernels.
-
-	* Add ftruncate() method.  This will only work with 2.6.15
-	(presumably) or later Linux kernels.
-
-	* Fix kernel module compile if kernel source and build directories
-	differ.  Report and initial patch by John Eastman
-
-2005-10-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: optimize buffer reallocation in fill_dir.
-
-2005-10-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.4.1
-
-2005-10-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: add debug for write result (by Shaun Jackman) and
-	warnings for too large read/write result
-
-2005-10-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Spelling fixes, thanks to Ioannis Barkas
-
-2005-10-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fuse_common.h: use extern "C".  Thanks to Valient Gough for the
-	patch
-
-2005-10-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* highlevel-lib: init() and destroy() methods didn't have an
-	initialized fuse_context.  Bug reported by Tim Stoakes
-
-2005-10-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.4.0
-
-2005-10-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add documentation to fuse_lowlevel.h
-
-	* API cleanups:
-
-	* Remove definitions of unused FATTR_CTIME / FUSE_SET_ATTR_CTIME
-
-	* Move fuse_mount() and fuse_unmount() to fuse_common.h
-
-	* Change the return type of fuse_reply_none() from int to void.
-
-2005-09-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: NFS exporting leaked dentries.  Bug found and fixed by
-	Akshat Aranya.
-
-2005-09-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: fix error message, when unable to open /dev/fuse.
-	Report by Balázs Pozsár
-
-2005-09-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* UClibc fixes from Christian Magnusson
-
-2005-09-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added NAME="%k" to util/udev.rules.  Fix by Mattias Wadman.
-
-2005-09-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.4.0-rc1
-
-2005-09-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: allow user umount in the case when /etc/mtab is a
-	symlink to /proc/mounts.  Reported by Balázs Pozsár.
-
-2005-09-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check for special node ID values in lookup and creation
-
-2005-09-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Slight optimization in returning EINVAL error in case of an open
-	with O_DIRECT flag.
-
-2005-09-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove '--enable-auto-modprobe' configure flag.  Module
-	auto-loading is now handled by the kernel.
-
-2005-09-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Install UDEV rule file, so /dev/fuse is created with mode 0666.
-	Help from Jens M. Noedler.
-
-2005-09-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add memory cleanup on thread exit
-
-2005-09-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Set umask to zero in fusexmp and fusexmp_fh, so that
-	files/directories are created with the requested mode.
-
-2005-09-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't ignore read error in multithreaded loop
-
-2005-09-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.4.0-pre2
-
-2005-09-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Revert lock and access operations.  Postpone these until 2.5.
-
-2005-09-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compile warning on 2.6.13 and later
-
-	* Fix compilation on old kernels
-
-2005-08-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: always refresh directory contents after rewinddir() to
-	conform to SUS.  Bug found by John Muir.
-
-2005-08-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.4.0-pre1
-
-2005-08-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: cleaned up (or messed up, depending on your POV) the low
-	level library API.  Hopefully this is close to the final form.
-
-2005-08-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: don't allow empty mountpoint argument, which defeats
-	automatic umounting in fuse_main().  Bugreport by Václav Jůza
-
-2005-08-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fix warnings in fuse.h and fuse_lowlevel.h if -Wshadow compiler
-	option is used (Paul Alfille).
-
-2005-08-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* highlevel-lib: added mount options "attr_timeout" and
-	"entry_timeout".  These options control the length of time file
-	attributes and entries (names) are cached.  Both default to 1.0
-	second.
-
-	* kernel: correctly handle zero timeout for attributes and entries
-
-2005-08-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added missing symbols to versionscript (Joshua J. Berry)
-
-	* kernel: implement two flags, open can set: 'direct_io' and
-	'keep_cache'.  These correspond exactly to mount options
-	'direct_io' and 'kernel_cache', but allow a per-open setting.
-
-	* Move 'direct_io' and 'kernel_cache' mount option handling to
-	userspace.  For both mount options, if the option is given, then
-	the respective open flag is set, otherwise the open flag is left
-	unmodified (so the filesystem can set it).
-
-	* lib (highlevel): make open method optional
-
-2005-07-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: invalidate attributes for read/readdir/readlink
-	operations
-
-	* kernel: detect newer UML kernels
-
-2005-07-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make the installation path of fuse.ko and mount.fuse
-	configurable through INSTALL_MOD_PATH and MOUNT_FUSE_PATH
-	environment variables.  Requirement and help from Csaba Henk.
-
-2005-07-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug, that causes filesystem requests to hang when unique
-	request counter becomes negative.  This happens after
-	2,147,483,648 operations, so most people won't care.  Thanks to
-	Franco Broi for the report and testing.
-
-2005-07-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't change mtime/ctime/atime to local time on read/write.
-	Bug reported by Ben Grimm
-
-	* Install fuse_common.h and fuse_lowlevel.h.  Report by Christian
-	Magnusson
-
-	* fusermount: use getopt_long() for option parsing.  It allows the
-	use of '--' to stop argument scanning, so fusermount can now
-	operate on directories whose names begin with a '-'.  Patch by
-	Adam Connell
-
-2005-07-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: add '-v', '--version' and '--help' options
-
-	* add inode based API
-
-2005-07-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: don't block signals in worker threads.  Problem noticed by
-	Usarin Heininga
-
-2005-07-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: don't allow both 'allow_other' and 'allow_root' options to
-	be given
-
-2005-07-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: check if mountpoint is empty (only '.' and '..' for
-	directories, and size = 0 for regular files).  If "nonempty"
-	option is given, omit this check.  This is useful, so users don't
-	accidentally hide data (e.g. from backup programs).  Thanks to
-	Frank van Maarseveen for pointing this out.
-
-	* kernel: check if mandatory mount options ('fd', 'rootmode',
-	'user_id', 'group_id') are all given
-
-	* lib: simplify 'readdir_ino' handling
-
-	* lib: add mount options 'umask=M', 'uid=N', 'gid=N'
-
-2005-07-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: clean up 'direct_io' code
-
-2005-06-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add 'mount.fuse' written by Petr Klima
-
-	* '/dev/fuse' is created by 'make install' if does not yet exist
-
-2005-06-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix UCLIBC compile error.  Patch by Christian Magnusson
-
-2005-06-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Enable the auto-loading of the module via access to the
-	corresponding device file.  Patch by Takashi Iwai.
-
-	* Allow mounting a regular file (over a regular file) for
-	unprivleged users.
-
-	* Do not create temporary device file.  Require "/dev/fuse" to
-	exist, and be readable/writable by the mounting user.
-
-2005-06-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3.0
-
-2005-06-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix serious information leak: if the filesystem returns a short
-	byte count to a read request, and there are non-zero number of
-	pages which are not filled at all, these pages will not be zeroed.
-	Hence the user can read out previous memory contents.  Found by
-	Sven Tantau.
-
-2005-05-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add "readdir_ino" mount option, which tries to fill in the d_ino
-	field in struct dirent.  This mount option is ignored if "use_ino"
-	is used.  It helps some programs (e.g. 'pwd' used over NFS from a
-	non-Linux OS).  Patch by David Shaw.
-
-2005-05-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-rc1
-
-2005-05-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* File save in krusader and other editors doesn't work with sshfs,
-	because open() is interrupted by a periodic signal, and open()
-	restarts forever, without any progress.  This could just be fixed
-	in open(), but the problem is more generic: if signals are
-	received more often than the filesystem can get the request to
-	userspace, it will never finish.  This is probably only a
-	theoretical problem, nevertheless I'm removing the possibility to
-	interrupt requests with anything other than SIGKILL, even before
-	being sent to userspace.  Bugreport by Eduard Czimbalmos.
-
-2005-05-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: add "tree_lock" rwlock, that is locked for write in
-	rename, unlink and rmdir, and locked for read in all other
-	operations.  This should fix the rename/release race reported by
-	Valient Gough and others.  The solution is very coarse, a finer
-	grained locking scheme could be implemented, but it would be much
-	more complex.  Let's see whether this is good enough.
-
-2005-05-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre7
-
-2005-05-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Better fix for out of order FORGET messages.  Now the
-	LOOKUP/FORGET messages are balanced exactly (one FORGET can
-	balance many lookups), so the order no longer matters.  This
-	changes the kernel ABI slightly, but the library remains backward
-	compatible.
-
-2005-05-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix abort for out of order FORGET messages.  Again.  Spotted by
-	Franco Broi again.  Sorry :)
-
-2005-04-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre6
-
-2005-04-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make fusermount work with fuse kernel modules not yet supporting
-	the "group_id" option (added for the purpose of stricter
-	permission checking).
-
-2005-04-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check for hard-linked directories in lookup.  This could cause
-	problems in the VFS, which assumes that such objects never exist.
-
-	* Make checking of permission for other users more strict.  Now
-	the same privilege is required for the mount owner as for ptrace
-	on the process performing the filesystem operation.
-
-2005-04-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre5
-
-2005-04-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add -msoft-float to kernel module compile flags for 2.4.X.  This
-	is needed on certain architectures.  Report from Chris Kirby
-
-	* Fix buggy behavior of open(..., O_CREAT|O_EXCL) if interrupted.
-	Reported by David Shaw
-
-	* Remove "allow_root" option from kernel module, and implement
-	it's functionality in the library
-
-	* Fix Oops caused by premature release of fuse_conn.  Clean up
-	related code, to be more readable
-
-	* Sendfile should not use page cache if "direct_io" mount option
-	is given
-
-2005-04-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix Oops in case of nfs export.  Spotted by David Shaw
-
-	* Fix another Oops in case of write over nfs with direct_io turned
-	on.  Again spotted by David Shaw
-
-2005-04-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre4
-
-2005-04-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* lib: finalized new readdir() interface, which now supersedes the
-	getdir() method.
-
-2005-04-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre3
-
-2005-04-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Implement backward compatibility with version 5 kernel ABI
-
-2005-04-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre2
-
-2005-04-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix dirent offset handling
-
-	* lib: add readdir and releasedir methods
-
-	* lib: use fh field of fuse_file_info in opendir, readdir,
-	releasedir and fsyncdir methods
-
-	* lib: check kernel API version and bail out of it's old.  This
-	will be properly fixed in the next release
-
-2005-03-31  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.3-pre1
-
-2005-03-31  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel API: add padding to structures, so 64bit and 32bit
-	compiler will return the same size
-
-	* kernel API: add offset field to fuse_dirent.  This will allow
-	more sophisticated readdir interface for userspace
-
-	* kernel API: change major number to 6
-
-	* kernel: fix warnings on 64bit archs
-
-	* kernel: in case of API version mismatch, return ECONNREFUSED
-
-2005-03-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: trivial cleanups
-
-2005-03-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fsyncdir() operation
-
-2005-03-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: add locking to background list (fixes previous fix)
-
-2005-03-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix bug which could cause leave busy inodes after
-	unmount, and Oops.
-
-2005-03-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* examples: add -lpthread to link flags to work around valgrind
-	quirk
-
-	* lib: don't exit threads, so cancelation doesn't cause segfault
-
-2005-03-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: fix nasty bug which could cause an Oops under certain
-	situations.  Found by Magnus Johansson
-
-2005-02-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: added opendir() method.  This can be used in case
-	permission checking in getdir() is too late.  Thanks to Usarin
-	Heininga for pointing out this deficiency
-
-	* libfuse: added init() and destroy() methods to fuse_operations
-
-	* kernel: llseek() method for files and directories made explicit
-
-	* kernel: fixed inode leak in NFS export in case of nodeid
-	wrapping
-
-2005-02-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse: clean up some unitialized memory found with valgrind
-
-	* Add -lpthread to Libs in fuse.pc.  Valgrind seems to need an
-	explicitly linked libpthread for applications
-
-2005-02-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: set umask, otherwise /etc/mtab will have
-	unpredictable permission.  Spotted by Jindrich Kolorenc
-
-	* fusermount: set owner and group of /etc/mtab to original values
-	on unmount
-
-	* libfuse: add 'use_ino' option to help.  Patch by Valient Gough
-
-2005-02-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Cleaned up directory reading (temporary file is not used)
-
-2005-02-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2
-
-2005-02-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix possible race when operation is interrupted
-
-2005-01-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compilation on 2.6.7
-
-2005-01-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2-pre6
-
-2005-01-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug in link() operation which caused the wrong path to be
-	passed as the first argument.  Found by Anton Altaparmakov
-
-2005-01-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* LIB: fix double reply in readdir operation
-
-	* fusermount: fix uid checking bug.  Patch by Adam Connell
-
-	* KERNEL: fix compile on various RedHat patched 2.4 kernels.
-	Patch by Keshava Gowda
-
-2005-01-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL: provide correct llseek semantics for fuse device (fixes
-	a bug on Progeny 2.4.20 kernel).  Reported by Valient Gough
-
-2005-01-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2-pre5 (matches kernel 2.6.11-rc1-mm2)
-
-2005-01-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL ABI: remove GETDIR operation, and add OPENDIR, READDIR
-	and RELEASEDIR.  This ends the ugly hack of passing a file
-	descriptor to the kernel, and actually makes the code simpler.
-
-2005-01-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2-pre4
-
-2005-01-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: remove capability setting, which was the cause of
-	problems for some users.  It seems that FS related capabilities
-	are removed by setfsuid(), so this isn't even needed.
-
-2005-01-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fix compilation on 2.4 kernels (reported by Valient Gough)
-
-	* fix failure to unmount bug (found by David Shaw)
-
-	* fusermount: improve parsing of /etc/fuse.conf
-
-2005-01-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove 'mount_max' and 'user_allow_other' module options.  These
-	are now checked by fusermount, and can be set in /etc/fuse.conf
-
-	* KERNEL: change check for fsid == 0 to capable(CAP_DAC_OVERRIDE)
-
-2005-01-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL: fix possible inode allocation problem, where
-	sizeof(struct inode) is not aligned (found by Mike Waychison)
-
-	* KERNEL: use new follow_link/put_link methods
-
-	* KERNEL: cosmetic fixes
-
-2005-01-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2-pre3
-
-2005-01-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add missing code that was accidently left out
-
-2005-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2-pre2
-
-2005-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Change "uid" mount option to "user_id" to avoid confusion with a
-	mount option "uid" commonly used by many filesystems
-
-2005-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.2-pre1
-
-2005-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* If FUSE is configured in the kernel, don't build it by default
-
-2005-01-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Compile fix by Christian Magnusson
-
-2005-01-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compilation for 2.6.{0-5} kernels
-
-2005-01-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL: if request is interrupted, still keep reference to used
-	inode(s) and file, so that FORGET and RELEASE are not sent until
-	userspace finishes the request.
-
-	* remove /{sys,proc}/fs/fuse/version, and instead add an INIT
-	request with the same information, which is more flexible,
-	simpler, works on embedded systems.
-
-2004-12-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL ABI: update interface to make it independent of type
-	sizes.  This will help on 64 bit architectures which can run
-	legacy 32 bit applications.
-
-	* KERNEL ABI: add "len" field to request headers.  This will allow
-	sending/receiving requests in multiple chunks.
-
-	* KERNEL: handle file type change more intelligently
-
-	* LIB: "-o debug" option should disable backgrounding (fix by
-	Fabien Reygrobellet)
-
-2004-12-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL: invalidate dentry/attributes if interrupted request
-	could leave filesystem in an unknown state.
-
-2004-12-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* KERNEL: lots of cleanups related to avoiding possible deadlocks.
-	These will cause some regressions, but stability is considered
-	more important.  If any of these features turns out to be
-	important, it can be readded with the deadlock problems addressed.
-
-	* Make all requests interruptible (only with SIGKILL currently).
-	This can be used to break any deadlock produced by the userspace
-	filesystem accessing it's own exported files.  The RELEASE request
-	is special, because if it's interrupted before sending it to
-	userspace it is still sent, but the reply is not awaited.
-
-	* If request is interrupted before being sent to userspace, and if
-	it hasn't yet got any side effects, it is always restarted,
-	regardless of the SA_RESTART flag.  This makes these interruptions
-	transparent to the process.
-
-	* Remove shared-writable mmap support, which was prone to an
-	out-of-memory deadlock situation
-
-	* Remove INVALIDATE userspace initiated request
-
-	* Make readpages() synchronous.  Asynchronous requests are
-	deadlock prone, since they cannot be interrupted.
-
-	* Add readv/writev support to fuse device operations
-
-	* Remove some printks, which userspace FS can use for a DoS
-	against syslog
-
-	* Remove 'large_read' mount option from 2.6 in kernel, check it in
-	fusermount instead
-
-	* LIB: improve compatibility with a fuse.h header installed in
-	${prefix}/include which in turn includes the real header.
-
-	* LIB: improve compatibility by defining fuse_main() (which is now
-	not used), so old configure scripts find it.
-
-2004-12-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* When mounting on a subdirectory of / don't duplicate slashes at
-	the beggining of path (spotted by David Shaw)
-
-2004-12-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug causing garbage in mount options (spotted by David Shaw)
-
-2004-12-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add 'writepage' flag to 'fuse_file_info'.
-
-	* More comments in fuse.h
-
-	* Get rid of double underscores
-
-2004-12-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add -D_FILE_OFFSET_BITS=64 to cflags provided by pkg-config
-
-	* helper.c: add -ho option, which only displays the options not
-	the usage header.  This can be used by filesystems which have
-	their own options.
-
-2004-12-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add source compatibility to 2.1 and 1.1 APIs.  To select betwen
-	versions simply define FUSE_USE_VERSION to 22, 21 or 11 before
-	including the fuse header
-
-	* Add binary compatibility to 2.1 version of library with symbol
-	versioning
-
-2004-12-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.1
-
-2004-12-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: clean up writing functions
-
-	* kernel: no allocation on write in direct_io mode
-
-	* move linux/fuse.h to fuse_kernel.h
-
-2004-11-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: clean up reading functions
-
-2004-11-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel: make readpage() uninterruptible
-
-	* kernel: check readonly filesystem flag in fuse_permission
-
-	* lib: don't die if version file not found and new style device
-	exists
-
-	* lib: add '-r' option, which is short for '-o ro'
-
-	* fusermount: simplify device opening
-
-	* kernel: when direct_io is turend on, copy data directly to
-	destination without itermediate buffer.  More efficient and safer,
-	since no allocation is done.
-
-	* fusermount: fix warning if fuse module is not loaded
-
-	* kernel: use /dev/fuse on 2.4 too
-
-2004-11-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* libfuse API change: open, read, write, flush, fsync and release
-	are passed a 'struct fuse_file_info' pointer containing the open
-	flags (open and release), and the file handle.  Verion changed to
-	3.0.
-
-2004-11-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* More cleanups in the kernel
-
-	* The 10,229 charater device number has been assigned for FUSE
-
-	* Version file checking fix (reported by Christian Magnusson)
-
-	* fusermount: opening the fuse device now doesn't need /sys.
-
-	* Optimize reading by controlling the maximum readahead based on
-	the 'max_read' mount option
-
-	* fixes for UCLIBC (Christian Magnusson)
-
-2004-11-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Cleaned up kernel in preparation for merge into mainline:
-
-	* Use /sys/fs/fuse/version instead of /proc/fs/fuse/version
-
-	* Use real device (/dev/fuse) instead of /proc/fs/fuse/dev
-
-	* __user annotations for sparse
-
-	* allocate individual pages instead of kmalloc in fuse_readdir,
-	fuse_read and fuse_write.
-
-	* Fix NFS export in case "use_ino" mount option is given
-
-	* Make libfuse and fusermount compatible with future versions
-
-	* fusermount: properly add mount options to /etc/mtab
-
-2004-11-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fusermount: do not resolve last component of mountpoint on if it
-	is '.' or '..'.  This new path resolvation is now done on mount as
-	well as unmount.  This enables relative paths to work on unmount.
-
-	* fusermount: parse common mount options like "ro", "rw", etc...
-
-	* Allow module params to be changed through sysfs
-
-2004-11-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 2.1-pre1
-
-2004-11-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug in fuse_readpages() causing Oops in certain situations.
-	Bug found by Vincenzo Ciancia.
-
-	* Fix compilation with kernels versions > 2.6.9.
-
-2004-11-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check kernel interface version in fusermount to prevent
-	strangeness in case of mismatch.
-
-	* No need to allocate fuse_conn until actual mount happens
-
-	* Fix potential race between umount and fuse_invalidate
-
-	* Check superblock of proc file in addition to inode number
-
-	* Fix race between request_send_noreply() and fuse_dev_release()
-
-2004-11-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Separate configure for the kernel directory
-
-	* Don't allow write to return more than 'count'
-
-	* Extend kernel interface for future use
-
-2004-11-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix 'makeconf.sh' to use autoreconf if available
-
-2004-11-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add ino argument to 'fuse_dirfil_t'.  NOTE: This breaks source
-	compatibility with earlier versions.  To compile earier versions
-	just add '-DFUSE_DIRFIL_COMPAT' compile flag or fix the source.
-	Do not use the "use_ino" mount flag with filesystems compiled with
-	FUSE_DIRFIL_COMPAT.
-
-	* Add pkg-config support.  To compile a FUSE based filesystem you
-	can do  "gcc -Wall `pkg-config --cflags --libs fuse` myfs.c -o myfs"
-	or similar.  Note, that the PKG_CONFIG_PATH environment variable
-	usually needs to be set to "/usr/local/lib/pkgconfig".
-
-	* fuse.h is now installed in ${prefix}/include/fuse/
-
-2004-11-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added "use_ino" mount option.  This enables the filesystems to
-	set the st_ino field on files
-
-2004-11-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix compile problems with ancient (<=2.4.18) kernels (reported
-	by Jeremy Smith)
-
-	* Add "allow_root" mount option.  Patch by Yaroslav Rastrigin
-
-	* Clear the 'exited' flag when mail loop is finished
-
-2004-10-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make xattr functions work under 2.6 (bug found by Vincenzo
-	Ciancia)
-
-2004-10-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Reset request in fuse_flush() (bugreport by David Shaw)
-
-2004-10-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fuse_main() now does not exit on error, rather it returns an
-	error code
-
-	* Exported __fuse_setup() and __fuse_teardown() functions, which
-	make it easier to implement a custom event loop.
-
-	* Use daemon() call to background the filesystem after mounting.
-	This function closes the standard input, output and error and
-	changes the current working directory to "/".
-
-2004-10-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 1.9
-
-2004-10-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Don't allow fuse_flush() to be interrupted (bug found by David
-	Shaw)
-
-2004-09-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add PID to fuse_context.  Patch by Steven James
-
-	* Change file handle type to 'unsigned long' in kernel interface
-
-2004-09-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* A slight API change: fuse_get_context() doesn't need the "fuse"
-	pointer, but the returned context contains it instead.  The
-	fuse_get() function is not needed anymore, so it's removed.
-
-	* Fix mounting and umounting FUSE filesystem under another FUSE
-	filesystem by non-root (bug spotted by Valient Gough)
-
-2004-09-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix deadlock in case of memory allocation failure.  Patch by
-	Christian Magnusson
-
-2004-09-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check memory allocation failures in libfuse
-
-2004-09-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check temporary file creation failure in do_getdir().  Bug
-	spotted by Terje Oseberg
-
-2004-09-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Allow "large_read" option for 2.6 kernels but warn of deprecation
-
-	* Make requests non-interruptible so race with FORGET is avoided.
-	This is only a temporary solution
-
-	* Support compiling FUSE kernel module on 2.4.x UML kernels
-
-2004-09-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug in case two FORGETs for the same node are executed in
-	the wrong order.  Bug spotted and endured for months by Franco
-	Broi, and logfile for solution provided by Terje Oseberg
-
-2004-09-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add -D_REENTRANT to the compile flags
-
-	* Add documentation of fuse internals by Terje Oseberg
-
-2004-08-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Change release method to be non-interruptible.  Fixes bug
-	causing missing release() call when program which has opened files
-	is killed (reported by Franco Broi and David Shaw)
-
-2004-07-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add fuse_invalidate() to library API
-
-2004-07-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check permissions in setattr if 'default_permissions' flag is
-	set.  Bug spotted by Damjan Lango
-
-2004-07-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* 'large_read' mount option removed for 2.6 kernels, since the
-	default (dynamic read size) is better
-
-	* Extend kernel API with file handles.  A file handle is returned
-	by open, and passed to read, write, flush, fsync and release.
-	This is currently only used for debug output in the library.
-
-	* Security changes:
-
-	* Change the current directory to the mountpoint before checking
-	the permissions and mount filesystem on "."
-
-	* By default don't modprobe the fuse module for non-root.  The old
-	behavior can be restored with the '--enable-auto-modprobe' flag of
-	./configure
-
-	* By default don't allow shared writable mappings for non-root.
-	The old behavior can be restored with the 'user_mmap=1' module
-	parameter
-
-2004-07-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Clean up mount option passing to fusermount and to fuse_new()
-	BEWARE: this changes the userspace API slightly, and the command
-	line usage of programs using fuse_main()
-
-2004-07-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Optimize reading under 2.6 kernels by issuing multiple page
-	asynchronous read requests
-
-2004-07-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Only use redirty_page_for_writepage() for kernels >= 2.6.6
-
-2004-07-16  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Separate directory entry and inode attribute validity timer
-
-	* New write semaphore to stop page writeback during truncate
-
-	* Fsync now waits for all writes to complete before sending the
-	request
-
-	* Optimization: if a page is completely written by
-	fuse_commit_write(), clear the dirty flag and set the uptodate
-	flag for that page
-
-	* Some memory cleanup at exit
-
-2004-07-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add FUSE_HARD_REMOVE flag, and '-i' option to fuse main, which
-	disable the "hide if open" behavior of unlink/rename.
-
-	* If temporary buffer allocation fails in raw read, fall back to a
-	smaller buffer
-
-2004-07-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix bug in do_open() in libfuse: open count was incremented
-	after the reply is sent so it could race with unlink/forget and
-	cause an abort.
-
-2004-07-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* When performing create or remove operation, refresh the parent's
-	attributes on next revalidate, as i_nlink (and maybe size/time)
-	could be inacurate.
-
-	* Use redirty_page_for_writepage() in fuse_writepage() for skipped
-	pages (2.6 only)
-
-	* Set set_page_dirty address space operation (2.6 only)
-
-2004-07-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Minor fix in read:  print debug info even if read size is zero
-
-2004-07-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix race between truncate and writepage (fsx-linux now runs
-	without error)
-
-2004-07-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix kernel hang on mkfifo under 2.4 kernels (spotted and patch
-	by Mattias Wadman)
-
-	* Added option for direct read/write (-r)
-
-	* Fix revalidate time setting for newly created inodes
-
-	* Remove uid==0 check for '-x' option in fusermount (kernel checks
-	this)
-
-	* fuse_main() only installs handlers for signals (out of INT, HUP,
-	TERM, PIPE), for which no handler has yet been installed
-
-	* Add module option 'user_allow_other' which if set to non-zero
-	will allow non root user to specify the 'allow_other' mount option
-	('-x' option of fusermount)
-
-	* Fix deadlock between page writeback completion and truncate
-	(bug found by Valient Gough with the fsx-linux utility)
-
-2004-07-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Change passing fuse include dir to 2.6 kernel make system more
-	robust (fixes compile problems seen on SuSE 9.1 with updated 2.6
-	kernel)
-
-2004-06-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Acquire inode->i_sem before open and release methods to prevent
-	concurrent rename or unlink operations.
-
-	* Make __fuse_read_cmd() read only one command.  This allows
-	multiplexing the fuse file descriptor with other event sources
-	using select() or poll() (patch by Jeff Harris)
-
-	* Export 'exited' flag with __fuse_exited() (patch by Jeff Harris)
-
-2004-06-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix file offset wrap around at 4G when doing large reads
-
-2004-06-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix memory leak in open (Valient Gough)
-
-2004-06-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add "close after delete" support to libfuse (patch by Valient
-	Gough)
-
-	* Cancel all worker threads before exit in multithreaded mode
-
-2004-06-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix locking bugs
-
-	* Don't send reply to RELEASE
-
-	* Work with newer libtool (1.5a)
-
-	* Check for st_atim member of struct stat
-
-2004-06-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* No request allocation needed on inode and file release
-
-2004-06-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix possible inode leak in userspace in case of unfinished
-	lookup/mknod/mkdir/symlink/link operation.
-
-2004-06-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix some races and cleanups in fuse_read_super()
-
-2004-06-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Requests are allocated at open time
-
-2004-06-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Build shared library as well as static (using libtool)
-
-	* Change FUSE_MINOR_VERSION from 1 to 0.  I know it's illegal but
-	there has not been a release with the previous minor number, and I
-	hope nobody is using it for anything.
-
-	* Change fuse_main(), so that default behavior is to go into
-	background if mount is successful.  '-f' and '-d' options disable
-	backgrounding.  This fixes the "Why does my FUSE daemon hang?"
-	newbie complaint.
-
-	* Cache ENOSYS (function not implemented) errors on *xattr, flush
-	and fsync
-
-	* Don't call getdir method from open() only from first readdir().
-	Open is sometimes just used to store the current directory
-	(e.g. find)
-
-2004-05-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added flush() call
-
-2004-05-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Extended attributes support for 2.4 (patch by Cody Pisto)
-
-2004-04-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fixed parser with modversions (Mattias Wadman)
-
-2004-04-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added mount option parser to 2.4 build
-
-2004-04-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Replaced binary mount data with text options
-
-	* Show FUSE specific mount options in /proc/mounts
-
-	* Check in fuse.h whether _FILE_OFFSET_BITS is set to 64
-
-2004-04-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Check some limits so userspace won't get too big requests
-
-2004-04-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Kill compile warning
-
-	* Upgraded user-mount patch for 2.6.5
-
-2004-04-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add detection of user-mode-linux to configure
-
-2004-03-31  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fixed zero size case for getxattr and listxattr
-
-2004-03-30  Miklos Szeredi <miklos@szeredi.hu>
-
-	* new fusermount flag '-z': lazy unmount, default is not lazy
-
-	* Extended attributes operations added (getxattr, setxattr,
-	listxattr, removexattr)
-
-2004-03-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* If filesystem doesn't define a statfs operation, then an
-	all-zero default statfs is returned instead of ENOSYS
-
-2004-03-24  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add FS_BINARY_MOUNTDATA filesystem flag for kernels > 2.6.4
-
-2004-03-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix for uClinux (Christian Magnusson)
-
-2004-03-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fuse_main() adds "-n progname" to the fusermount command line
-
-	* More kernel interface changes:
-
-	* Lookup/getattr return cache timeout values
-
-2004-02-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Clean up option parsing in fuse_main()
-
-	* Added fuse_get() function which returns the fuse object created
-	by fuse_main()
-
-2004-02-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* removed old way of mounting (fusermount mountpoint program)
-
-	* more kernel interface changes:
-
-	* added nanosecond precision to file times
-
-	* removed interface version from mount data
-
-	* added /proc/fs/fuse/version which contains MAJOR.MINOR
-
-2004-02-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* statfs library API changed to match other methods.  Since this
-	  is not backward compatible FUSE_MAJOR_VERSION is changed to 2
-
-	* kernel interface changes follow:
-
-	* statfs changed to 64 bits, added 'bavail' field
-
-	* add generation number to lookup result
-
-	* optimized mknod/mkdir/symlink/link (no separate lookup is
-	needed)
-
-	* rdev size increased to 32 bits for mknod
-
-	* kernel interface version changed to 3.1
-
-2004-02-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* user-mount upgraded for 2.6.3 kernel
-
-2004-02-17  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added user-mount.2.6.2-rc3.patch
-
-	* Add FS_SAFE flag to fuse filesystem
-
-	* fusermount should allow (un)mounting for non-root even if not
-	suid-root
-
-2004-02-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Remove MS_PERMISSION mount flag (that means something else now)
-
-2004-02-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added check for i_size_read/write functions to configure.in
-	(patch by Valient Gough)
-
-2004-02-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fixed writing >= 2G files
-
-	* Check file size on open (with generic_file_open())
-
-	* Readpage calls flush_dcache_page() after storing data
-
-	* Use i_size_read/write for accessing inode->i_size
-
-	* Make loopback mount of a fuse file work
-
-2004-02-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 1.1
-
-2004-01-29  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Properly check if the inode exists in fuse_invalidate
-
-2004-01-27  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added -q option for fusermount
-
-	* fuse_unmount() now uses -q option of fusermount, so no error is
-	printed if the cause of the program exit is that the filesystem
-	has already been unmounted
-
-	* Fix i_nlink correctness after rmdir/unlink
-
-2004-01-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 1.1-pre2
-
-2004-01-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix typo (thanks Marcos Dione)
-
-	* Compile fixes for 2.4 kernels
-
-2004-01-23  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix CONFIG_MODVERSIONS compile on 2.6
-
-2004-01-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Write all pending data before a RELEASE operation
-
-	* Suppress 'Bad file descriptor' warning on exit
-
-	* Replaced fusermount option '-d xxx' with '-n xxx' so it doesn't
-	get confused with '-d' of fuse_main() (sorry about this change)
-
-	* New fusermount option '-l' which enables big reads.  Big reads
-	are now disabled by default.
-
-	* fuse_main() can accept fusermount arguments after a '--'
-
-2004-01-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Support for exporting filesystem over NFS (see README.NFS)
-
-2004-01-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Support non-blocking writepage on 2.6.  This makes FUSE behave
-	much more nicely in low-memory situations
-
-	* Fix 32-bit dev handling in getattr and mknod for 2.6 kernels.
-	(Note: the mknod method does not yet use 32bit device number)
-
-2004-01-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Code cleanups
-
-2004-01-07  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Released 1.1-pre1
-
-2004-01-06  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Integrated 2.6 kernel support patch by Michael Grigoriev
-
-	* Improvements and cleanups for 2.6 kernels
-
-2004-01-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added -d option to fusermount
-
-2003-12-15  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added major+minor version to library API, and minor version to
-	  kernel API
-
-2003-12-13  David McNab <david@rebirthing.co.nz>
-
-	* Implemented fsync support in examples/example.py
-
-	* Implemented 'fsync' and 'statfs' methods in python
-	  interface
-
-2003-12-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Make it compile on 2.4.19.
-
-	* Add fsync operation (write file failed on xemacs & vi)
-
-2003-12-12  David McNab <david@rebirthing.co.nz>
-
-	* Added distutils support to the python module, as per standard
-	  python development practice
-
-2003-12-11  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Add file locking for mount/unmount (based on patch by Valient
-	Gough)
-
-2003-12-11  David McNab <david@rebirthing.co.nz>
-
-	* Python filesystem - was broken with python2.3, now fixed:
-	   - changed PyTuple_* calls to PySequence_*, because os.lstat
-	     is no longer returning a pure tuple
-	   - changed PyInt_Check() calls to also call PyLong_Check,
-	     to cover for cases (eg os.lstat) where longs are returned
-	   - Added support for file 'release' handling, which IMO is
-	     essential since this signals to a FS that writes to a file
-	     are complete (and therefore the file can now be disposed of
-	     meaningfully at the python filesystem's discretion)
-	   - Added '__init__' handler to base Fuse class, which allows
-	     your Python class to know the mountpoint and mount args,
-	     as attributes myfs.mountpoint, myfs.optlist, myfs.optdict
-
-	* General:
-	   - added 'mount.fuse' script (in util/ dir), which is meant to be
-	     symlinked from /sbin, and which allows FUSE filesystems to
-	     be mounted with the 'mount' command, and listed in fstab;
-	     also, mount arguments get passed to your filesystem
-
-
-2003-11-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix kernel version detection (again).  Bugreport by Peter Levart
-
-2003-11-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Applied read combining patch by Michael Grigoriev (tested by
-	Valient Gough and Vincent Wagelaar)
-
-2003-10-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Mtab handling fix in fusermount by "Valient Gough" (SF patch
-	#766443)
-
-2003-10-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Error code fixes in kernel module
-
-2003-10-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* kernel version detection fix
-
-	* fusermount now uses "lazy" umount option
-
-	* fusermount can use modprobe with module-init-tools
-
-2003-09-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Integrated caching patch by Michael Grigoriev
-
-	* Added "Filesystems" file with descriptions of projects using
-	FUSE
-
-	* Added patch by Michael Grigoriev to allow compliation of FUSE
-	kernel module for 2.6 kernels
-
-2003-06-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* And another spec-file fix by Achim Settelmeier
-
-2003-05-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Spec-file fix by Achim Settelmeier
-
-2003-03-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fix umount oops (found by Samuli Kärkkäinen)
-
-2003-03-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Merge of fuse_redhat.spec and fuse.spec by Achim Settelmeier
-
-2003-03-04  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Updated fuse.spec file (Achim Settelmeier)
-
-2003-02-19  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Version 1.0 released
-
-2003-02-12  Miklos Szeredi <miklos@szeredi.hu>
-
-	* SuSE compilation fix by Juan-Mariano de Goyeneche
-
-2002-12-10  Miklos Szeredi <miklos@szeredi.hu>
-
-	* The release() VFS call is now exported to the FUSE interface
-
-2002-12-05  Miklos Szeredi <miklos@szeredi.hu>
-
-	* 64 bit file offset fixes in the fuse kernel module
-
-	* Added function 'fuse_exit()' which can be used to exit the main
-	loop
-
-2002-12-03  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added _FILE_OFFSET_BITS=64 define to fuse.h.  Note, that this is
-	an incompatible interface change.
-
-2002-10-28  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Portablility fix (bug reported by C. Chris Erway)
-
-2002-10-25  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Use Mark Glines' fd passing method for default operation instead
-	of old reexec
-
-2002-10-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fix "Stale NFS file handle" bug caused by changes in 2.4.19
-
-2002-10-22  Miklos Szeredi <miklos@szeredi.hu>
-
-	* fix incompatiblity with Red Hat kernels, with help from Nathan
-	Thompson-Amato.
-
-2002-04-18  Mark Glines <mark@glines.org>
-
-	* added an alternative to fuse_mount(), called
-      fuse_mount_ioslave(), which does not need to reexec the
-      FUSE program.
-	* added a small helper util needed by fuse_mount_ioslave().
-
-2002-03-16  Mark Glines <mark@glines.org>
-
-	* use struct fuse_statfs everywhere possible to avoid problems
-      with the headerfiles changing struct statfs member sizes
-
-2002-03-01  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Another RPM spec file for RedHat >= 7 by Ian Pilcher
-
-2002-01-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* RPM support by Achim Settelmeier
-
-2002-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Version 0.95 released
-
-2002-01-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Revaidate all path components not just the last, this means a
-	very small performance penalty for being more up-to-date.
-
-2002-01-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Update and fix python interface
-
-2002-01-07  Mark Glines <mark@glines.org>
-
-	* Added statfs() support to kernel, lib, examples, and perl!
-
-2001-12-26  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Better cross compilation support
-
-	* Ported to Compaq IPAQ
-
-2001-12-20  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added function fuse_get_context() to library API (inspired by
-	patch from Matt Ryan)
-
-	* Added flags to fusermount and to kernel interface to control
-	permission checking
-
-	* Integrated fuse_set_operations() into fuse_new()
-
-2001-12-08  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Applied header protection + extern "C" patch by Roland
-	Bauerschmidt
-
-2001-12-02  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Added perl bindings by Mark Glines
-
-2001-11-21  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Cleaned up way of mounting simple filesystems.
-
-	* fuse_main() helper function added
-
-2001-11-18  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Optimized read/write operations, so that minimal copying of data
-	is done
-
-2001-11-14  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Python bindings by Jeff Epler added
-
-2001-11-13  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Fixed vfsmount reference leak in fuse_follow_link
-
-	* FS blocksize is set to PAGE_CACHE_SIZE, blksize attribute from
-	userspace is ignored
-
-2001-11-09  Miklos Szeredi <miklos@szeredi.hu>
-
-	* Started ChangeLog
diff --git a/libfuse/Makefile b/libfuse/Makefile
index 522ca60..eb20b68 100644
--- a/libfuse/Makefile
+++ b/libfuse/Makefile
@@ -31,14 +31,12 @@ INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
 
 AR ?= ar
 
-SRC   = \
+SRC_C = \
 	lib/buffer.c \
 	lib/crc32b.c \
 	lib/debug.c \
 	lib/fuse.c \
 	lib/fuse_dirents.c \
-	lib/fuse_kern_chan.c \
-	lib/fuse_loop_mt.c \
 	lib/fuse_lowlevel.c \
 	lib/fuse_mt.c \
 	lib/fuse_node.c \
@@ -47,8 +45,13 @@ SRC   = \
 	lib/fuse_signals.c \
 	lib/helper.c \
 	lib/mount.c
-OBJS = $(SRC:lib/%.c=build/%.o)
-DEPS = $(SRC:lib/%.c=build/%.d)
+SRC_CPP = \
+	lib/fuse_loop_mt.cpp \
+	lib/fuse_msgbuf.cpp
+OBJS_C   = $(SRC_C:lib/%.c=build/%.o)
+OBJS_CPP = $(SRC_CPP:lib/%.cpp=build/%.o)
+DEPS_C   = $(SRC_C:lib/%.c=build/%.d)
+DEPS_CPP = $(SRC_CPP:lib/%.cpp=build/%.d)
 CFLAGS ?= \
 	$(OPT_FLAGS)
 CFLAGS := \
@@ -57,6 +60,12 @@ CFLAGS := \
 	-Wall \
 	-pipe \
 	-MMD
+CXXFLAGS := \
+	${CXXFLAGS} \
+	-std=c++11 \
+	-Wall \
+	-pipe \
+	-MMD
 FUSERMOUNT_DIR = $(BINDIR)
 FUSE_FLAGS = \
 	-Iinclude \
@@ -81,10 +90,10 @@ build/stamp:
 	touch $@
 
 objects: build/config.h
-	$(MAKE) $(OBJS)
+	$(MAKE) $(OBJS_C) $(OBJS_CPP)
 
 build/libfuse.a: objects
-	${AR} rcs build/libfuse.a $(OBJS)
+	${AR} rcs build/libfuse.a $(OBJS_C) $(OBJS_CPP)
 
 utils: mergerfs-fusermount mount.mergerfs
 
@@ -101,6 +110,9 @@ mount.mergerfs: build/mount.mergerfs
 build/%.o: lib/%.c
 	$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
 
+build/%.o: lib/%.cpp
+	$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
+
 clean:
 	rm -rf build
 
@@ -120,4 +132,4 @@ install: $(INSTALLUTILS)
 
 .PHONY: objects strip utils install install-utils
 
--include $(DEPS)
+-include $(DEPS_C) $(DEPS_CPP)
diff --git a/libfuse/include/fuse.h b/libfuse/include/fuse.h
index 3e6df2e..32e5ca8 100644
--- a/libfuse/include/fuse.h
+++ b/libfuse/include/fuse.h
@@ -475,9 +475,10 @@ struct fuse_operations
    *
    * Introduced in version 2.9
    */
-  int (*write_buf) (const fuse_file_info_t *ffi,
-                    struct fuse_bufvec     *buf,
-                    off_t                   off);
+  int (*write) (const fuse_file_info_t *ffi,
+                const char             *data,
+                size_t                  size,
+                off_t                   off);
 
   /** Store data from an open file in a buffer
    *
@@ -640,7 +641,8 @@ void fuse_destroy(struct fuse *f);
  */
 void fuse_exit(struct fuse *f);
 
-int fuse_config_num_threads(const struct fuse *fuse_);
+int fuse_config_read_thread_count(const struct fuse *f);
+int fuse_config_process_thread_count(const struct fuse *f);
 
 /**
  * FUSE event loop with multiple threads
@@ -729,101 +731,6 @@ struct fuse_fs;
  * fuse_fs_releasedir and fuse_fs_statfs, which return 0.
  */
 
-int fuse_fs_getattr(struct fuse_fs  *fs,
-                    const char      *path,
-                    struct stat     *buf,
-                    fuse_timeouts_t *timeout);
-
-int fuse_fs_fgetattr(struct fuse_fs   *fs,
-                     struct stat      *buf,
-                     fuse_file_info_t *fi,
-                     fuse_timeouts_t  *timeout);
-
-int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
-                   const char *newpath);
-int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
-int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
-int fuse_fs_symlink(struct fuse_fs *fs,
-                    const char *linkname,
-		    const char *path,
-                    struct stat *st,
-                    fuse_timeouts_t *timeouts);
-int fuse_fs_link(struct fuse_fs *fs,
-                 const char *oldpath,
-                 const char *newpath,
-                 struct stat *st,
-                 fuse_timeouts_t *timeouts);
-int fuse_fs_release(struct fuse_fs *fs,
-                    fuse_file_info_t *fi);
-int fuse_fs_open(struct fuse_fs *fs, const char *path,
-                 fuse_file_info_t *fi);
-int fuse_fs_read_buf(struct fuse_fs *fs,
-                     struct fuse_bufvec **bufp, size_t size, off_t off,
-                     fuse_file_info_t *fi);
-int fuse_fs_write_buf(struct fuse_fs *fs,
-                      struct fuse_bufvec *buf, off_t off,
-                      fuse_file_info_t *fi);
-int fuse_fs_fsync(struct fuse_fs *fs, int datasync,
-                  fuse_file_info_t *fi);
-int fuse_fs_flush(struct fuse_fs *fs,
-                  fuse_file_info_t *fi);
-int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
-int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
-                    fuse_file_info_t *fi);
-int fuse_fs_readdir(struct fuse_fs        *fs,
-                    fuse_file_info_t *fi,
-                    fuse_dirents_t        *buf);
-int fuse_fs_fsyncdir(struct fuse_fs *fs, int datasync,
-                     fuse_file_info_t *fi);
-int fuse_fs_releasedir(struct fuse_fs *fs,
-                       fuse_file_info_t *fi);
-int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
-                   fuse_file_info_t *fi);
-int fuse_fs_lock(struct fuse_fs *fs,
-                 fuse_file_info_t *fi, int cmd, struct flock *lock);
-int fuse_fs_flock(struct fuse_fs *fs,
-                  fuse_file_info_t *fi, int op);
-int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode);
-int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid);
-int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size);
-int fuse_fs_ftruncate(struct fuse_fs *fs, off_t size,
-                      fuse_file_info_t *fi);
-int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
-                    const struct timespec tv[2]);
-int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
-int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf,
-                     size_t len);
-int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode,
-                  dev_t rdev);
-int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode);
-int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name,
-                     const char *value, size_t size, int flags);
-int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name,
-                     char *value, size_t size);
-int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list,
-                      size_t size);
-int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
-                        const char *name);
-int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
-                 uint64_t *idx);
-int fuse_fs_ioctl(struct fuse_fs *fs, unsigned long cmd, void *arg,
-                  fuse_file_info_t *fi, unsigned int flags,
-                  void *data, uint32_t *out_bufsz);
-int fuse_fs_poll(struct fuse_fs *fs,
-                 fuse_file_info_t *fi, fuse_pollhandle_t *ph,
-                 unsigned *reventsp);
-int fuse_fs_fallocate(struct fuse_fs *fs, int mode,
-                      off_t offset, off_t length, fuse_file_info_t *fi);
-void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
-void fuse_fs_destroy(struct fuse_fs *fs);
-
-int fuse_fs_prepare_hide(struct fuse_fs *fs, const char *path, uint64_t *fh);
-int fuse_fs_free_hide(struct fuse_fs *fs, uint64_t fh);
-ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs,
-                                fuse_file_info_t *fi_in, off_t off_in,
-                                fuse_file_info_t *fi_out, off_t off_out,
-                                size_t len, int flags);
-
 int fuse_notify_poll(fuse_pollhandle_t *ph);
 
 /**
@@ -856,12 +763,6 @@ struct fuse *fuse_setup(int argc, char *argv[],
 /** This is the part of fuse_main() after the event loop */
 void fuse_teardown(struct fuse *fuse, char *mountpoint);
 
-/** Read a single command.  If none are read, return NULL */
-struct fuse_cmd *fuse_read_cmd(struct fuse *f);
-
-/** Process a single command */
-void fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd);
-
 /** Multi threaded event loop, which calls the custom command
     processor function */
 int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
diff --git a/libfuse/include/fuse_common.h b/libfuse/include/fuse_common.h
index 6208950..7d9a050 100644
--- a/libfuse/include/fuse_common.h
+++ b/libfuse/include/fuse_common.h
@@ -126,6 +126,7 @@ struct fuse_file_info_t
 #define FUSE_CAP_POSIX_ACL         (1 << 19)
 #define FUSE_CAP_CACHE_SYMLINKS    (1 << 20)
 #define FUSE_CAP_MAX_PAGES         (1 << 21)
+#define FUSE_CAP_SETXATTR_EXT      (1 << 22)
 
 /**
  * Ioctl flags
diff --git a/libfuse/include/fuse_kernel.h b/libfuse/include/fuse_kernel.h
index 373cada..1e0495b 100644
--- a/libfuse/include/fuse_kernel.h
+++ b/libfuse/include/fuse_kernel.h
@@ -172,6 +172,31 @@
  *  - add FUSE_WRITE_KILL_PRIV flag
  *  - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
  *  - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
+ *
+ *  7.32
+ *  - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
+ *
+ *  7.33
+ *  - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
+ *  - add FUSE_OPEN_KILL_SUIDGID
+ *  - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
+ *  - add FUSE_SETXATTR_ACL_KILL_SGID
+ *
+ *  7.34
+ *  - add FUSE_SYNCFS
+ *
+ *  7.35
+ *  - add FOPEN_NOFLUSH
+ *
+ *  7.36
+ *  - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
+ *  - add flags2 to fuse_init_in and fuse_init_out
+ *  - add FUSE_SECURITY_CTX init flag
+ *  - add security context to create, mkdir, symlink, and mknod requests
+ *  - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
+ *
+ *  7.37
+ *  - add FUSE_TMPFILE
  */
 
 #ifndef _LINUX_FUSE_H
@@ -207,7 +232,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 31
+#define FUSE_KERNEL_MINOR_VERSION 37
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -231,7 +256,7 @@ struct fuse_attr {
 	uint32_t	gid;
 	uint32_t	rdev;
 	uint32_t	blksize;
-	uint32_t	padding;
+	uint32_t	flags;
 };
 
 struct fuse_kstatfs {
@@ -257,17 +282,18 @@ struct fuse_file_lock {
 /**
  * Bitmasks for fuse_setattr_in.valid
  */
-#define FATTR_MODE	(1 << 0)
-#define FATTR_UID	(1 << 1)
-#define FATTR_GID	(1 << 2)
-#define FATTR_SIZE	(1 << 3)
-#define FATTR_ATIME	(1 << 4)
-#define FATTR_MTIME	(1 << 5)
-#define FATTR_FH	(1 << 6)
-#define FATTR_ATIME_NOW	(1 << 7)
-#define FATTR_MTIME_NOW	(1 << 8)
-#define FATTR_LOCKOWNER	(1 << 9)
-#define FATTR_CTIME	(1 << 10)
+#define FATTR_MODE         (1 << 0)
+#define FATTR_UID          (1 << 1)
+#define FATTR_GID          (1 << 2)
+#define FATTR_SIZE         (1 << 3)
+#define FATTR_ATIME        (1 << 4)
+#define FATTR_MTIME        (1 << 5)
+#define FATTR_FH           (1 << 6)
+#define FATTR_ATIME_NOW    (1 << 7)
+#define FATTR_MTIME_NOW    (1 << 8)
+#define FATTR_LOCKOWNER    (1 << 9)
+#define FATTR_CTIME        (1 << 10)
+#define FATTR_KILL_SUIDGID (1 << 11)
 
 /**
  * Flags returned by the OPEN request
@@ -277,12 +303,14 @@ struct fuse_file_lock {
  * FOPEN_NONSEEKABLE: the file is not seekable
  * FOPEN_CACHE_DIR: allow caching this directory
  * FOPEN_STREAM: the file is stream-like (no file position at all)
+ * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
  */
 #define FOPEN_DIRECT_IO		(1 << 0)
 #define FOPEN_KEEP_CACHE	(1 << 1)
 #define FOPEN_NONSEEKABLE	(1 << 2)
 #define FOPEN_CACHE_DIR		(1 << 3)
 #define FOPEN_STREAM		(1 << 4)
+#define FOPEN_NOFLUSH		(1 << 5)
 
 /**
  * INIT request/reply flags
@@ -313,7 +341,21 @@ struct fuse_file_lock {
  * FUSE_CACHE_SYMLINKS: cache READLINK responses
  * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
  * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
- * FUSE_MAP_ALIGNMENT: map_alignment field is valid
+ * FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for
+ *		       foffset and moffset fields in struct
+ *		       fuse_setupmapping_out and fuse_removemapping_one.
+ * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
+ * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc.
+ *			Upon write/truncate suid/sgid is only killed if caller
+ *			does not have CAP_FSETID. Additionally upon
+ *			write/truncate sgid is killed only if file has group
+ *			execute permission. (Same as Linux VFS behavior).
+ * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
+ * FUSE_INIT_EXT: extended fuse_init_in request
+ * FUSE_INIT_RESERVED: reserved, do not use
+ * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
+ *			mknod
+ * FUSE_HAS_INODE_DAX:  use per inode DAX
  */
 #define FUSE_ASYNC_READ		(1 << 0)
 #define FUSE_POSIX_LOCKS	(1 << 1)
@@ -342,6 +384,14 @@ struct fuse_file_lock {
 #define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
 #define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
 #define FUSE_MAP_ALIGNMENT	(1 << 26)
+#define FUSE_SUBMOUNTS		(1 << 27)
+#define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
+#define FUSE_SETXATTR_EXT	(1 << 29)
+#define FUSE_INIT_EXT		(1 << 30)
+#define FUSE_INIT_RESERVED	(1 << 31)
+/* bits 32..63 get shifted down 32 bits into the flags2 field */
+#define FUSE_SECURITY_CTX	(1ULL << 32)
+#define FUSE_HAS_INODE_DAX	(1ULL << 33)
 
 /**
  * CUSE INIT request/reply flags
@@ -371,11 +421,14 @@ struct fuse_file_lock {
  *
  * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
  * FUSE_WRITE_LOCKOWNER: lock_owner field is valid
- * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits
+ * FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits
  */
 #define FUSE_WRITE_CACHE	(1 << 0)
 #define FUSE_WRITE_LOCKOWNER	(1 << 1)
-#define FUSE_WRITE_KILL_PRIV	(1 << 2)
+#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
+
+/* Obsolete alias; this flag implies killing suid/sgid only. */
+#define FUSE_WRITE_KILL_PRIV	FUSE_WRITE_KILL_SUIDGID
 
 /**
  * Read flags
@@ -417,6 +470,27 @@ struct fuse_file_lock {
  */
 #define FUSE_FSYNC_FDATASYNC	(1 << 0)
 
+/**
+ * fuse_attr flags
+ *
+ * FUSE_ATTR_SUBMOUNT: Object is a submount root
+ * FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode
+ */
+#define FUSE_ATTR_SUBMOUNT      (1 << 0)
+#define FUSE_ATTR_DAX		(1 << 1)
+
+/**
+ * Open flags
+ * FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable
+ */
+#define FUSE_OPEN_KILL_SUIDGID	(1 << 0)
+
+/**
+ * setxattr flags
+ * FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
+ */
+#define FUSE_SETXATTR_ACL_KILL_SGID	(1 << 0)
+
 enum fuse_opcode {
 	FUSE_LOOKUP		= 1,
 	FUSE_FORGET		= 2,  /* no reply */
@@ -465,6 +539,8 @@ enum fuse_opcode {
 	FUSE_COPY_FILE_RANGE	= 47,
 	FUSE_SETUPMAPPING	= 48,
 	FUSE_REMOVEMAPPING	= 49,
+	FUSE_SYNCFS		= 50,
+	FUSE_TMPFILE		= 51,
 
 	/* CUSE specific operations */
 	CUSE_INIT		= 4096,
@@ -578,14 +654,14 @@ struct fuse_setattr_in {
 
 struct fuse_open_in {
 	uint32_t	flags;
-	uint32_t	unused;
+	uint32_t	open_flags;	/* FUSE_OPEN_... */
 };
 
 struct fuse_create_in {
 	uint32_t	flags;
 	uint32_t	mode;
 	uint32_t	umask;
-	uint32_t	padding;
+	uint32_t	open_flags;	/* FUSE_OPEN_... */
 };
 
 struct fuse_open_out {
@@ -647,9 +723,13 @@ struct fuse_fsync_in {
 	uint32_t	padding;
 };
 
+#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
+
 struct fuse_setxattr_in {
 	uint32_t	size;
 	uint32_t	flags;
+	uint32_t	setxattr_flags;
+	uint32_t	padding;
 };
 
 struct fuse_getxattr_in {
@@ -684,6 +764,8 @@ struct fuse_init_in {
 	uint32_t	minor;
 	uint32_t	max_readahead;
 	uint32_t	flags;
+	uint32_t	flags2;
+	uint32_t	unused[11];
 };
 
 #define FUSE_COMPAT_INIT_OUT_SIZE 8
@@ -700,7 +782,8 @@ struct fuse_init_out {
 	uint32_t	time_gran;
 	uint16_t	max_pages;
 	uint16_t	map_alignment;
-	uint32_t	unused[8];
+	uint32_t	flags2;
+	uint32_t	unused[7];
 };
 
 #define CUSE_INIT_INFO_MAX 4096
@@ -808,9 +891,12 @@ struct fuse_dirent {
 	char name[];
 };
 
-#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
-#define FUSE_DIRENT_ALIGN(x) \
+/* Align variable length records to 64bit boundary */
+#define FUSE_REC_ALIGN(x) \
 	(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
 #define FUSE_DIRENT_SIZE(d) \
 	FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
 
@@ -869,7 +955,8 @@ struct fuse_notify_retrieve_in {
 };
 
 /* Device ioctls: */
-#define FUSE_DEV_IOC_CLONE	_IOR(229, 0, uint32_t)
+#define FUSE_DEV_IOC_MAGIC		229
+#define FUSE_DEV_IOC_CLONE		_IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
 
 struct fuse_lseek_in {
 	uint64_t	fh;
@@ -892,4 +979,60 @@ struct fuse_copy_file_range_in {
 	uint64_t	flags;
 };
 
+#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
+#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
+struct fuse_setupmapping_in {
+	/* An already open handle */
+	uint64_t	fh;
+	/* Offset into the file to start the mapping */
+	uint64_t	foffset;
+	/* Length of mapping required */
+	uint64_t	len;
+	/* Flags, FUSE_SETUPMAPPING_FLAG_* */
+	uint64_t	flags;
+	/* Offset in Memory Window */
+	uint64_t	moffset;
+};
+
+struct fuse_removemapping_in {
+	/* number of fuse_removemapping_one follows */
+	uint32_t        count;
+};
+
+struct fuse_removemapping_one {
+	/* Offset into the dax window start the unmapping */
+	uint64_t        moffset;
+	/* Length of mapping required */
+	uint64_t	len;
+};
+
+#define FUSE_REMOVEMAPPING_MAX_ENTRY   \
+		(PAGE_SIZE / sizeof(struct fuse_removemapping_one))
+
+struct fuse_syncfs_in {
+	uint64_t	padding;
+};
+
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ */
+struct fuse_secctx {
+	uint32_t	size;
+	uint32_t	padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+	uint32_t	size;
+	uint32_t	nr_secctx;
+};
+
 #endif /* _LINUX_FUSE_H */
diff --git a/libfuse/include/fuse_lowlevel.h b/libfuse/include/fuse_lowlevel.h
index 9fe0b65..8785968 100644
--- a/libfuse/include/fuse_lowlevel.h
+++ b/libfuse/include/fuse_lowlevel.h
@@ -19,12 +19,14 @@
  * 25
  */
 
+#include "fuse_msgbuf.h"
 #ifndef FUSE_USE_VERSION
 #define FUSE_USE_VERSION 24
 #endif
 
 #include "extern_c.h"
 #include "fuse_common.h"
+#include "fuse_kernel.h"
 
 #include <fcntl.h>
 #include <stdint.h>
@@ -117,12 +119,6 @@ struct fuse_ctx
   mode_t umask;
 };
 
-struct fuse_forget_data
-{
-  uint64_t ino;
-  uint64_t nlookup;
-};
-
 /* ----------------------------------------------------------- *
  * Request methods and replies				       *
  * ----------------------------------------------------------- */
@@ -183,7 +179,8 @@ struct fuse_lowlevel_ops
    * @param parent inode number of the parent directory
    * @param name the name to look up
    */
-  void (*lookup)(fuse_req_t req, uint64_t parent, const char *name);
+  void (*lookup)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Forget about an inode
@@ -221,7 +218,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param nlookup the number of lookups to forget
    */
-  void (*forget)(fuse_req_t req, uint64_t ino, uint64_t nlookup);
+  void (*forget)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Get file attributes
@@ -229,13 +227,9 @@ struct fuse_lowlevel_ops
    * Valid replies:
    *   fuse_reply_attr
    *   fuse_reply_err
-   *
-   * @param req request handle
-   * @param ino the inode number
-   * @param fi for future use, currently always NULL
    */
-  void (*getattr)(fuse_req_t req, uint64_t ino,
-                  fuse_file_info_t *fi);
+  void (*getattr)(fuse_req_t              req,
+                  struct fuse_in_header  *hdr);
 
   /**
    * Set file attributes
@@ -264,8 +258,8 @@ struct fuse_lowlevel_ops
    * Changed in version 2.5:
    *     file information filled in for ftruncate
    */
-  void (*setattr)(fuse_req_t req, uint64_t ino, struct stat *attr,
-                  int to_set, fuse_file_info_t *fi);
+  void (*setattr)(fuse_req_t             req,
+                  struct fuse_in_header *hdr);
 
   /**
    * Read symbolic link
@@ -277,7 +271,8 @@ struct fuse_lowlevel_ops
    * @param req request handle
    * @param ino the inode number
    */
-  void (*readlink)(fuse_req_t req, uint64_t ino);
+  void (*readlink)(fuse_req_t             req,
+                   struct fuse_in_header *hdr);
 
   /**
    * Create file node
@@ -295,8 +290,8 @@ struct fuse_lowlevel_ops
    * @param mode file type and mode with which to create the new file
    * @param rdev the device number (only valid if created file is a device)
    */
-  void (*mknod)(fuse_req_t req, uint64_t parent, const char *name,
-                mode_t mode, dev_t rdev);
+  void (*mknod)(fuse_req_t             req,
+                struct fuse_in_header *hdr);
 
   /**
    * Create a directory
@@ -310,8 +305,8 @@ struct fuse_lowlevel_ops
    * @param name to create
    * @param mode with which to create the new file
    */
-  void (*mkdir)(fuse_req_t req, uint64_t parent, const char *name,
-                mode_t mode);
+  void (*mkdir)(fuse_req_t             req,
+                struct fuse_in_header *hdr);
 
   /**
    * Remove a file
@@ -328,7 +323,8 @@ struct fuse_lowlevel_ops
    * @param parent inode number of the parent directory
    * @param name to remove
    */
-  void (*unlink)(fuse_req_t req, uint64_t parent, const char *name);
+  void (*unlink)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Remove a directory
@@ -345,7 +341,8 @@ struct fuse_lowlevel_ops
    * @param parent inode number of the parent directory
    * @param name to remove
    */
-  void (*rmdir)(fuse_req_t req, uint64_t parent, const char *name);
+  void (*rmdir)(fuse_req_t             req,
+                struct fuse_in_header *hdr);
 
   /**
    * Create a symbolic link
@@ -359,8 +356,8 @@ struct fuse_lowlevel_ops
    * @param parent inode number of the parent directory
    * @param name to create
    */
-  void (*symlink)(fuse_req_t req, const char *link, uint64_t parent,
-                  const char *name);
+  void (*symlink)(fuse_req_t             req,
+                  struct fuse_in_header *hdr);
 
   /** Rename a file
    *
@@ -379,8 +376,8 @@ struct fuse_lowlevel_ops
    * @param newparent inode number of the new parent directory
    * @param newname new name
    */
-  void (*rename)(fuse_req_t req, uint64_t parent, const char *name,
-                 uint64_t newparent, const char *newname);
+  void (*rename)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Create a hard link
@@ -394,8 +391,8 @@ struct fuse_lowlevel_ops
    * @param newparent inode number of the new parent directory
    * @param newname new name to create
    */
-  void (*link)(fuse_req_t req, uint64_t ino, uint64_t newparent,
-               const char *newname);
+  void (*link)(fuse_req_t             req,
+               struct fuse_in_header *hdr);
 
   /**
    * Open a file
@@ -422,8 +419,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param fi file information
    */
-  void (*open)(fuse_req_t req, uint64_t ino,
-               fuse_file_info_t *fi);
+  void (*open)(fuse_req_t             req,
+               struct fuse_in_header *hdr);
 
   /**
    * Read data
@@ -450,8 +447,8 @@ struct fuse_lowlevel_ops
    * @param off offset to read from
    * @param fi file information
    */
-  void (*read)(fuse_req_t req, uint64_t ino, size_t size, off_t off,
-               fuse_file_info_t *fi);
+  void (*read)(fuse_req_t             req,
+               struct fuse_in_header *hdr);
 
   /**
    * Write data
@@ -476,8 +473,8 @@ struct fuse_lowlevel_ops
    * @param off offset to write to
    * @param fi file information
    */
-  void (*write)(fuse_req_t req, uint64_t ino, const char *buf,
-                size_t size, off_t off, fuse_file_info_t *fi);
+  void (*write)(fuse_req_t             req,
+                struct fuse_in_header *hdr);
 
   /**
    * Flush method
@@ -508,8 +505,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param fi file information
    */
-  void (*flush)(fuse_req_t req, uint64_t ino,
-                fuse_file_info_t *fi);
+  void (*flush)(fuse_req_t             req,
+                struct fuse_in_header *hdr);
 
   /**
    * Release an open file
@@ -535,8 +532,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param fi file information
    */
-  void (*release)(fuse_req_t req, uint64_t ino,
-                  fuse_file_info_t *fi);
+  void (*release)(fuse_req_t             req,
+                  struct fuse_in_header *hdr);
 
   /**
    * Synchronize file contents
@@ -552,8 +549,8 @@ struct fuse_lowlevel_ops
    * @param datasync flag indicating if only data should be flushed
    * @param fi file information
    */
-  void (*fsync)(fuse_req_t req, uint64_t ino, int datasync,
-                fuse_file_info_t *fi);
+  void (*fsync)(fuse_req_t             req,
+                struct fuse_in_header *hdr);
 
   /**
    * Open a directory
@@ -576,8 +573,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param fi file information
    */
-  void (*opendir)(fuse_req_t req, uint64_t ino,
-                  fuse_file_info_t *fi);
+  void (*opendir)(fuse_req_t req,
+                  struct fuse_in_header *hdr);
 
   /**
    * Read directory
@@ -600,12 +597,11 @@ struct fuse_lowlevel_ops
    * @param off offset to continue reading the directory stream
    * @param fi file information
    */
-  void (*readdir)(fuse_req_t req, uint64_t ino, size_t size, off_t off,
-                  fuse_file_info_t *llffi);
+  void (*readdir)(fuse_req_t             req,
+                  struct fuse_in_header *hdr);
 
-  void (*readdir_plus)(fuse_req_t req, uint64_t ino,
-                       size_t size, off_t off,
-                       fuse_file_info_t *ffi);
+  void (*readdir_plus)(fuse_req_t             req,
+                       struct fuse_in_header *hdr);
 
   /**
    * Release an open directory
@@ -623,8 +619,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param fi file information
    */
-  void (*releasedir)(fuse_req_t req, uint64_t ino,
-                     fuse_file_info_t *fi);
+  void (*releasedir)(fuse_req_t             req,
+                     struct fuse_in_header *hdr);
 
   /**
    * Synchronize directory contents
@@ -643,8 +639,8 @@ struct fuse_lowlevel_ops
    * @param datasync flag indicating if only data should be flushed
    * @param fi file information
    */
-  void (*fsyncdir)(fuse_req_t req, uint64_t ino, int datasync,
-                   fuse_file_info_t *fi);
+  void (*fsyncdir)(fuse_req_t             req,
+                   struct fuse_in_header *hdr);
 
   /**
    * Get file system statistics
@@ -656,7 +652,8 @@ struct fuse_lowlevel_ops
    * @param req request handle
    * @param ino the inode number, zero means "undefined"
    */
-  void (*statfs)(fuse_req_t req, uint64_t ino);
+  void (*statfs)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Set an extended attribute
@@ -664,8 +661,8 @@ struct fuse_lowlevel_ops
    * Valid replies:
    *   fuse_reply_err
    */
-  void (*setxattr)(fuse_req_t req, uint64_t ino, const char *name,
-                   const char *value, size_t size, int flags);
+  void (*setxattr)(fuse_req_t             req,
+                   struct fuse_in_header *hdr);
 
   /**
    * Get an extended attribute
@@ -690,8 +687,8 @@ struct fuse_lowlevel_ops
    * @param name of the extended attribute
    * @param size maximum size of the value to send
    */
-  void (*getxattr)(fuse_req_t req, uint64_t ino, const char *name,
-                   size_t size);
+  void (*getxattr)(fuse_req_t             req,
+                   struct fuse_in_header *hdr);
 
   /**
    * List extended attribute names
@@ -716,7 +713,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param size maximum size of the list to send
    */
-  void (*listxattr)(fuse_req_t req, uint64_t ino, size_t size);
+  void (*listxattr)(fuse_req_t             req,
+                    struct fuse_in_header *hdr);
 
   /**
    * Remove an extended attribute
@@ -728,7 +726,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param name of the extended attribute
    */
-  void (*removexattr)(fuse_req_t req, uint64_t ino, const char *name);
+  void (*removexattr)(fuse_req_t                   req,
+                      const struct fuse_in_header *hdr);
 
   /**
    * Check file access permissions
@@ -748,7 +747,8 @@ struct fuse_lowlevel_ops
    * @param ino the inode number
    * @param mask requested access mode
    */
-  void (*access)(fuse_req_t req, uint64_t ino, int mask);
+  void (*access)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Create and open a file
@@ -783,8 +783,8 @@ struct fuse_lowlevel_ops
    * @param mode file type and mode with which to create the new file
    * @param fi file information
    */
-  void (*create)(fuse_req_t req, uint64_t parent, const char *name,
-                 mode_t mode, fuse_file_info_t *fi);
+  void (*create)(fuse_req_t             req,
+                 struct fuse_in_header *hdr);
 
   /**
    * Test for a POSIX file lock
@@ -800,9 +800,8 @@ struct fuse_lowlevel_ops
    * @param fi file information
    * @param lock the region/type to test
    */
-  void (*getlk)(fuse_req_t req, uint64_t ino,
-                fuse_file_info_t *fi, struct flock *lock);
-
+  void (*getlk)(fuse_req_t                   req,
+                const struct fuse_in_header *hdr);
   /**
    * Acquire, modify or release a POSIX file lock
    *
@@ -848,8 +847,8 @@ struct fuse_lowlevel_ops
    * @param blocksize unit of block index
    * @param idx block index within file
    */
-  void (*bmap)(fuse_req_t req, uint64_t ino, size_t blocksize,
-               uint64_t idx);
+  void (*bmap)(fuse_req_t                   req,
+               const struct fuse_in_header *hdr);
 
   /**
    * Ioctl
@@ -878,9 +877,8 @@ struct fuse_lowlevel_ops
    * @param in_bufsz number of fetched bytes
    * @param out_bufsz maximum size of output data
    */
-  void (*ioctl)(fuse_req_t req, uint64_t ino, unsigned long cmd, void *arg,
-                fuse_file_info_t *fi, unsigned flags,
-                const void *in_buf, uint32_t in_bufsz, uint32_t out_bufsz);
+  void (*ioctl)(fuse_req_t                   req,
+                const struct fuse_in_header *hdr);
 
   /**
    * Poll for IO readiness
@@ -908,40 +906,8 @@ struct fuse_lowlevel_ops
    * @param fi file information
    * @param ph poll handle to be used for notification
    */
-  void (*poll)(fuse_req_t req,
-               uint64_t ino,
-               fuse_file_info_t *fi,
-               fuse_pollhandle_t *ph);
-
-  /**
-   * Write data made available in a buffer
-   *
-   * This is a more generic version of the ->write() method.  If
-   * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the
-   * kernel supports splicing from the fuse device, then the
-   * data will be made available in pipe for supporting zero
-   * copy data transfer.
-   *
-   * buf->count is guaranteed to be one (and thus buf->idx is
-   * always zero). The write_buf handler must ensure that
-   * bufv->off is correctly updated (reflecting the number of
-   * bytes read from bufv->buf[0]).
-   *
-   * Introduced in version 2.9
-   *
-   * Valid replies:
-   *   fuse_reply_write
-   *   fuse_reply_err
-   *
-   * @param req request handle
-   * @param ino the inode number
-   * @param bufv buffer containing the data
-   * @param off offset to write to
-   * @param fi file information
-   */
-  void (*write_buf)(fuse_req_t req, uint64_t ino,
-                    struct fuse_bufvec *bufv, off_t off,
-                    fuse_file_info_t *fi);
+  void (*poll)(fuse_req_t                   req,
+               const struct fuse_in_header *hdr);
 
   /**
    * Callback function for the retrieve request
@@ -957,8 +923,10 @@ struct fuse_lowlevel_ops
    * @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
    * @param bufv the buffer containing the returned data
    */
-  void (*retrieve_reply)(fuse_req_t req, void *cookie, uint64_t ino,
-                         off_t offset, struct fuse_bufvec *bufv);
+  void (*retrieve_reply)(fuse_req_t req,
+                         void *cookie,
+                         uint64_t ino,
+                         off_t offset);
 
   /**
    * Forget about multiple inodes
@@ -973,8 +941,8 @@ struct fuse_lowlevel_ops
    *
    * @param req request handle
    */
-  void (*forget_multi)(fuse_req_t req, size_t count,
-                       struct fuse_forget_data *forgets);
+  void (*forget_multi)(fuse_req_t             req,
+                       struct fuse_in_header *hdr);
 
   /**
    * Acquire, modify or release a BSD file lock
@@ -1013,8 +981,8 @@ struct fuse_lowlevel_ops
    * @param mode determines the operation to be performed on the given range,
    *             see fallocate(2)
    */
-  void (*fallocate)(fuse_req_t req, uint64_t ino, int mode,
-                    off_t offset, off_t length, fuse_file_info_t *fi);
+  void (*fallocate)(fuse_req_t                   req,
+                    const struct fuse_in_header *hdr);
 
   /**
    * Copy a range of data from one file to another
@@ -1052,15 +1020,8 @@ struct fuse_lowlevel_ops
    * @param len maximum size of the data to copy
    * @param flags passed along with the copy_file_range() syscall
    */
-  void (*copy_file_range)(fuse_req_t        req,
-                          uint64_t        ino_in,
-                          off_t             off_in,
-                          fuse_file_info_t *fi_in,
-                          uint64_t        ino_out,
-                          off_t             off_out,
-                          fuse_file_info_t *fi_out,
-                          size_t            len,
-                          int               flags);
+  void (*copy_file_range)(fuse_req_t                   req,
+                          const struct fuse_in_header *hdr);
 };
 
 /**
@@ -1163,7 +1124,8 @@ int fuse_reply_readlink(fuse_req_t req, const char *link);
  * @param fi file information
  * @return zero for success, -errno for failure to send reply
  */
-int fuse_reply_open(fuse_req_t req, const fuse_file_info_t *fi);
+int fuse_reply_open(fuse_req_t req,
+                    const fuse_file_info_t *fi);
 
 /**
  * Reply with number of bytes written
@@ -1503,231 +1465,32 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
  * Session interface					       *
  * ----------------------------------------------------------- */
 
-/**
- * Session operations
- *
- * This is used in session creation
- */
-struct fuse_session_ops
-{
-  /**
-   * Hook to process a request (mandatory)
-   *
-   * @param data user data passed to fuse_session_new()
-   * @param buf buffer containing the raw request
-   * @param len request length
-   * @param ch channel on which the request was received
-   */
-  void (*process)(void *data, const char *buf, size_t len,
-                  struct fuse_chan *ch);
-
-  /**
-   * Hook for session exit and reset (optional)
-   *
-   * @param data user data passed to fuse_session_new()
-   * @param val exited status (1 - exited, 0 - not exited)
-   */
-  void (*exit)(void *data, int val);
-
-  /**
-   * Hook for querying the current exited status (optional)
-   *
-   * @param data user data passed to fuse_session_new()
-   * @return 1 if exited, 0 if not exited
-   */
-  int (*exited)(void *data);
-
-  /**
-   * Hook for cleaning up the channel on destroy (optional)
-   *
-   * @param data user data passed to fuse_session_new()
-   */
-  void (*destroy)(void *data);
-};
-
-/**
- * Create a new session
- *
- * @param op session operations
- * @param data user data
- * @return new session object, or NULL on failure
- */
-struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data);
-
-/**
- * Assign a channel to a session
- *
- * Note: currently only a single channel may be assigned.  This may
- * change in the future
- *
- * If a session is destroyed, the assigned channel is also destroyed
- *
- * @param se the session
- * @param ch the channel
- */
+struct fuse_session *fuse_session_new(void *data,
+                                      void *receive_buf,
+                                      void *process_buf,
+                                      void *destroy);
 void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
-
-/**
- * Remove a channel from a session
- *
- * If the channel is not assigned to a session, then this is a no-op
- *
- * @param ch the channel to remove
- */
 void fuse_session_remove_chan(struct fuse_chan *ch);
-
-/**
- * Iterate over the channels assigned to a session
- *
- * The iterating function needs to start with a NULL channel, and
- * after that needs to pass the previously returned channel to the
- * function.
- *
- * @param se the session
- * @param ch the previous channel, or NULL
- * @return the next channel, or NULL if no more channels exist
- */
-struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
-                                         struct fuse_chan *ch);
-
-/**
- * Process a raw request
- *
- * @param se the session
- * @param buf buffer containing the raw request
- * @param len request length
- * @param ch channel on which the request was received
- */
-void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
-                          struct fuse_chan *ch);
-
-/**
- * Process a raw request supplied in a generic buffer
- *
- * This is a more generic version of fuse_session_process().  The
- * fuse_buf may contain a memory buffer or a pipe file descriptor.
- *
- * @param se the session
- * @param buf the fuse_buf containing the request
- * @param ch channel on which the request was received
- */
-void fuse_session_process_buf(struct fuse_session *se,
-                              const struct fuse_buf *buf, struct fuse_chan *ch);
-
-/**
- * Receive a raw request supplied in a generic buffer
- *
- * This is a more generic version of fuse_chan_recv().  The fuse_buf
- * supplied to this function contains a suitably allocated memory
- * buffer.  This may be overwritten with a file descriptor buffer.
- *
- * @param se the session
- * @param buf the fuse_buf to store the request in
- * @param chp pointer to the channel
- * @return the actual size of the raw request, or -errno on error
- */
-int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
-                             struct fuse_chan **chp);
-
-/**
- * Destroy a session
- *
- * @param se the session
- */
 void fuse_session_destroy(struct fuse_session *se);
-
-/**
- * Exit a session
- *
- * @param se the session
- */
 void fuse_session_exit(struct fuse_session *se);
-
-/**
- * Reset the exited status of a session
- *
- * @param se the session
- */
-void fuse_session_reset(struct fuse_session *se);
-
-/**
- * Query the exited status of a session
- *
- * @param se the session
- * @return 1 if exited, 0 if not exited
- */
 int fuse_session_exited(struct fuse_session *se);
-
-/**
- * Get the user data provided to the session
- *
- * @param se the session
- * @return the user data
- */
+void fuse_session_reset(struct fuse_session *se);
 void *fuse_session_data(struct fuse_session *se);
+int fuse_session_receive(struct fuse_session *se,
+                         struct fuse_buf *buf);
+void fuse_session_process(struct fuse_session *se,
+                          const void          *buf,
+                          const size_t         bufsize);
 
-/**
- * Enter a multi-threaded event loop
- *
- * @param se the session
- * @return 0 on success, -1 on error
- */
-int fuse_session_loop_mt(struct fuse_session *se, const int threads);
+int fuse_session_loop_mt(struct fuse_session *se,
+                         const int            read_thread_count,
+                         const int            process_thread_count);
 
 /* ----------------------------------------------------------- *
  * Channel interface					       *
  * ----------------------------------------------------------- */
 
-/**
- * Channel operations
- *
- * This is used in channel creation
- */
-struct fuse_chan_ops
-{
-  /**
-   * Hook for receiving a raw request
-   *
-   * @param ch pointer to the channel
-   * @param buf the buffer to store the request in
-   * @param size the size of the buffer
-   * @return the actual size of the raw request, or -1 on error
-   */
-  int (*receive)(struct fuse_chan **chp, char *buf, size_t size);
-
-  /**
-   * Hook for sending a raw reply
-   *
-   * A return value of -ENOENT means, that the request was
-   * interrupted, and the reply was discarded
-   *
-   * @param ch the channel
-   * @param iov vector of blocks
-   * @param count the number of blocks in vector
-   * @return zero on success, -errno on failure
-   */
-  int (*send)(struct fuse_chan *ch, const struct iovec iov[],
-              size_t count);
-
-  /**
-   * Destroy the channel
-   *
-   * @param ch the channel
-   */
-  void (*destroy)(struct fuse_chan *ch);
-};
-
-/**
- * Create a new channel
- *
- * @param op channel operations
- * @param fd file descriptor of the channel
- * @param bufsize the minimal receive buffer size
- * @param data user data
- * @return the new channel object, or NULL on failure
- */
-struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
-                                size_t bufsize, void *data);
+struct fuse_chan *fuse_chan_new(int fd, size_t bufsize);
 
 /**
  * Query the file descriptor of the channel
@@ -1761,37 +1524,6 @@ void *fuse_chan_data(struct fuse_chan *ch);
  */
 struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
 
-/**
- * Receive a raw request
- *
- * A return value of -ENODEV means, that the filesystem was unmounted
- *
- * @param ch pointer to the channel
- * @param buf the buffer to store the request in
- * @param size the size of the buffer
- * @return the actual size of the raw request, or -errno on error
- */
-int fuse_chan_recv(struct fuse_chan **ch, char *buf, size_t size);
-
-/**
- * Send a raw reply
- *
- * A return value of -ENOENT means, that the request was
- * interrupted, and the reply was discarded
- *
- * @param ch the channel
- * @param iov vector of blocks
- * @param count the number of blocks in vector
- * @return zero on success, -errno on failure
- */
-int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[],
-                   size_t count);
-
-/**
- * Destroy a channel
- *
- * @param ch the channel
- */
 void fuse_chan_destroy(struct fuse_chan *ch);
 
 EXTERN_C_END
diff --git a/libfuse/include/fuse_msgbuf.h b/libfuse/include/fuse_msgbuf.h
new file mode 100644
index 0000000..bd1f5a9
--- /dev/null
+++ b/libfuse/include/fuse_msgbuf.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef struct fuse_msgbuf_t fuse_msgbuf_t;
+struct fuse_msgbuf_t
+{
+  char     *mem;
+  uint32_t  size;
+  uint32_t  used;
+  int       pipefd[2];
+};
diff --git a/libfuse/include/fuse_pollhandle.h b/libfuse/include/fuse_pollhandle.h
new file mode 100644
index 0000000..76a6261
--- /dev/null
+++ b/libfuse/include/fuse_pollhandle.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <stdint.h>
+
+struct fuse_chan;
+struct fuse_ll;
+
+typedef struct fuse_pollhandle_t fuse_pollhandle_t;
+struct fuse_pollhandle_t
+{
+  uint64_t kh;
+  struct fuse_chan *ch;
+  struct fuse_ll *f;
+};
diff --git a/libfuse/lib/bounded_queue.hpp b/libfuse/lib/bounded_queue.hpp
new file mode 100644
index 0000000..3e58da7
--- /dev/null
+++ b/libfuse/lib/bounded_queue.hpp
@@ -0,0 +1,165 @@
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <utility>
+
+
+template<typename T>
+class BoundedQueue
+{
+public:
+  explicit
+  BoundedQueue(std::size_t max_size_,
+               bool        block_ = true)
+    : _block(block),
+      _max_size(max_size_)
+  {
+    if(_max_size == 0)
+      _max_size = 1;
+  }
+
+  bool
+  push(const T& item_)
+  {
+    {
+      std::unique_lock guard(_queue_lock);
+
+      _condition_push.wait(guard, [&]() { return _queue.size() < _max_size || !_block; });
+
+      if(_queue.size() == _max_size)
+        return false;
+
+      _queue.push(item);
+    }
+
+    _condition_pop.notify_one();
+
+    return true;
+  }
+
+  bool
+  push(T&& item_)
+  {
+    {
+      std::unique_lock guard(_queue_lock);
+
+      _condition_push.wait(guard, [&]() { return _queue.size() < _max_size || !_block; });
+
+      if(_queue.size() == _max_size)
+        return false;
+
+      _queue.push(std::move(item_));
+    }
+    _condition_pop.notify_one();
+    return true;
+  }
+
+  template<typename... Args>
+  bool
+  emplace(Args&&... args_)
+  {
+    {
+      std::unique_lock guard(_queue_lock);
+
+      _condition_push.wait(guard, [&]() { return _queue.size() < _max_size || !_block; });
+
+      if(_queue.size() == _max_size)
+        return false;
+
+      _queue.emplace(std::forward<Args>(args_)...);
+    }
+
+    _condition_pop.notify_one();
+
+    return true;
+  }
+
+  bool
+  pop(T& item_)
+  {
+    {
+      std::unique_lock guard(_queue_lock);
+
+      _condition_pop.wait(guard, [&]() { return !_queue.empty() || !_block; });
+      if(_queue.empty())
+        return false;
+
+      item_ = std::move(_queue.front());
+
+      _queue.pop();
+    }
+
+    _condition_push.notify_one();
+
+    return true;
+  }
+
+  std::size_t
+  size() const
+  {
+    std::lock_guard guard(_queue_lock);
+
+    return _queue.size();
+  }
+
+  std::size_t
+  capacity() const
+  {
+    return _max_size;
+  }
+
+  bool
+  empty() const
+  {
+    std::lock_guard guard(_queue_lock);
+
+    return _queue.empty();
+  }
+
+  bool
+  full() const
+  {
+    std::lock_guard lock(_queue_lock);
+
+    return (_queue.size() == capacity());
+  }
+
+  void
+  block()
+  {
+    std::lock_guard guard(_queue_lock);
+    _block = true;
+  }
+
+  void
+  unblock()
+  {
+    {
+      std::lock_guard guard(_queue_lock);
+      _block = false;
+    }
+
+    _condition_push.notify_all();
+    _condition_pop.notify_all();
+  }
+
+  bool
+  blocking() const
+  {
+    std::lock_guard guard(_queue_lock);
+
+    return _block;
+  }
+
+private:
+  mutable std::mutex _queue_lock;
+
+private:
+  bool _block;
+  std::queue<T> _queue;
+  const std::size_t _max_size;
+  std::condition_variable _condition_push;
+  std::condition_variable _condition_pop;
+};
diff --git a/libfuse/lib/buffer.c b/libfuse/lib/buffer.c
index 50ed9e7..ce7110f 100644
--- a/libfuse/lib/buffer.c
+++ b/libfuse/lib/buffer.c
@@ -182,8 +182,7 @@ static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
   }
 
   while (len) {
-    res = splice(src->fd, srcpos, dst->fd, dstpos, len,
-                 splice_flags);
+    res = splice(src->fd, srcpos, dst->fd, dstpos, len, splice_flags);
     if (res == -1) {
       if (copied)
         break;
diff --git a/libfuse/lib/fuse.c b/libfuse/lib/fuse.c
index 418795d..fdcdaae 100644
--- a/libfuse/lib/fuse.c
+++ b/libfuse/lib/fuse.c
@@ -22,11 +22,13 @@
 #include "fuse_lowlevel.h"
 #include "fuse_misc.h"
 #include "fuse_opt.h"
+#include "fuse_pollhandle.h"
 
 #include <assert.h>
 #include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <poll.h>
 #include <signal.h>
@@ -53,6 +55,8 @@
 
 #define NODE_TABLE_MIN_SIZE 8192
 
+#define PARAM(inarg) ((void*)(((char*)(inarg)) + sizeof(*(inarg))))
+
 static int g_LOG_METRICS = 0;
 
 struct fuse_config
@@ -63,12 +67,12 @@ struct fuse_config
   int remember;
   int debug;
   int nogc;
-  int use_ino;
   int set_mode;
   int set_uid;
   int set_gid;
   int help;
-  int threads;
+  int read_thread_count;
+  int process_thread_count;
 };
 
 struct fuse_fs
@@ -231,6 +235,13 @@ filename_free(struct fuse *f_,
   free(fn_);
 }
 
+static
+void*
+fuse_hdr_arg(const struct fuse_in_header *hdr_)
+{
+  return (void*)&hdr_[1];
+}
+
 static
 void
 list_add(struct list_head *new,
@@ -397,7 +408,7 @@ free_node(struct fuse *f_,
   filename_free(f_,node_->name);
 
   if(node_->is_hidden)
-    fuse_fs_free_hide(f_->fs,node_->hidden_fh);
+    f_->fs->op.free_hide(node_->hidden_fh);
 
   free_node_mem(f_,node_);
 }
@@ -1392,8 +1403,6 @@ set_stat(struct fuse *f,
          uint64_t     nodeid,
          struct stat *stbuf)
 {
-  if(!f->conf.use_ino)
-    stbuf->st_ino = nodeid;
   if(f->conf.set_mode)
     stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask);
   if(f->conf.set_uid)
@@ -1409,104 +1418,6 @@ req_fuse(fuse_req_t req)
   return (struct fuse*)fuse_req_userdata(req);
 }
 
-int
-fuse_fs_getattr(struct fuse_fs  *fs,
-                const char      *path,
-                struct stat     *buf,
-                fuse_timeouts_t *timeout)
-{
-  return fs->op.getattr(path,buf,timeout);
-}
-
-int
-fuse_fs_fgetattr(struct fuse_fs   *fs,
-                 struct stat      *buf,
-                 fuse_file_info_t *fi,
-                 fuse_timeouts_t  *timeout)
-{
-  return fs->op.fgetattr(fi,buf,timeout);
-}
-
-int
-fuse_fs_rename(struct fuse_fs *fs,
-               const char     *oldpath,
-               const char     *newpath)
-{
-  return fs->op.rename(oldpath,newpath);
-}
-
-int
-fuse_fs_prepare_hide(struct fuse_fs *fs_,
-                     const char     *path_,
-                     uint64_t       *fh_)
-{
-  return fs_->op.prepare_hide(path_,fh_);
-}
-
-int
-fuse_fs_free_hide(struct fuse_fs *fs_,
-                  uint64_t        fh_)
-{
-  return fs_->op.free_hide(fh_);
-}
-
-int
-fuse_fs_unlink(struct fuse_fs *fs,
-               const char     *path)
-{
-  return fs->op.unlink(path);
-}
-
-int
-fuse_fs_rmdir(struct fuse_fs *fs,
-              const char     *path)
-{
-  return fs->op.rmdir(path);
-}
-
-int
-fuse_fs_symlink(struct fuse_fs  *fs_,
-                const char      *linkname_,
-                const char      *path_,
-                struct stat     *st_,
-                fuse_timeouts_t *timeouts_)
-{
-  return fs_->op.symlink(linkname_,path_,st_,timeouts_);
-}
-
-int
-fuse_fs_link(struct fuse_fs  *fs,
-             const char      *oldpath,
-             const char      *newpath,
-             struct stat     *st_,
-             fuse_timeouts_t *timeouts_)
-{
-  return fs->op.link(oldpath,newpath,st_,timeouts_);
-}
-
-int
-fuse_fs_release(struct fuse_fs   *fs,
-                fuse_file_info_t *fi)
-{
-  return fs->op.release(fi);
-}
-
-int
-fuse_fs_opendir(struct fuse_fs   *fs,
-                const char       *path,
-                fuse_file_info_t *fi)
-{
-  return fs->op.opendir(path,fi);
-}
-
-int
-fuse_fs_open(struct fuse_fs   *fs,
-             const char       *path,
-             fuse_file_info_t *fi)
-{
-  return fs->op.open(path,fi);
-}
-
 static
 void
 fuse_free_buf(struct fuse_bufvec *buf)
@@ -1521,290 +1432,6 @@ fuse_free_buf(struct fuse_bufvec *buf)
     }
 }
 
-int
-fuse_fs_read_buf(struct fuse_fs      *fs,
-                 struct fuse_bufvec **bufp,
-                 size_t               size,
-                 off_t                off,
-                 fuse_file_info_t    *fi)
-{
-  int res;
-
-  res = fs->op.read_buf(fi,bufp,size,off);
-  if(res < 0)
-    return res;
-
-  return 0;
-}
-
-int
-fuse_fs_write_buf(struct fuse_fs     *fs,
-                  struct fuse_bufvec *buf,
-                  off_t               off,
-                  fuse_file_info_t   *fi)
-{
-  return fs->op.write_buf(fi,buf,off);
-}
-
-int
-fuse_fs_fsync(struct fuse_fs   *fs,
-              int               datasync,
-              fuse_file_info_t *fi)
-{
-  return fs->op.fsync(fi,datasync);
-}
-
-int
-fuse_fs_fsyncdir(struct fuse_fs   *fs,
-                 int               datasync,
-                 fuse_file_info_t *fi)
-{
-  return fs->op.fsyncdir(fi,datasync);
-}
-
-int
-fuse_fs_flush(struct fuse_fs   *fs,
-              fuse_file_info_t *fi)
-{
-  return fs->op.flush(fi);
-}
-
-int
-fuse_fs_statfs(struct fuse_fs *fs,
-               const char     *path,
-               struct statvfs *buf)
-{
-  return fs->op.statfs(path,buf);
-}
-
-int
-fuse_fs_releasedir(struct fuse_fs   *fs,
-                   fuse_file_info_t *fi)
-{
-  return fs->op.releasedir(fi);
-}
-
-int
-fuse_fs_readdir(struct fuse_fs   *fs,
-                fuse_file_info_t *fi,
-                fuse_dirents_t   *buf)
-{
-  return fs->op.readdir(fi,buf);
-}
-
-int
-fuse_fs_readdir_plus(struct fuse_fs   *fs_,
-                     fuse_file_info_t *ffi_,
-                     fuse_dirents_t   *buf_)
-{
-  return fs_->op.readdir_plus(ffi_,buf_);
-}
-
-int
-fuse_fs_create(struct fuse_fs   *fs,
-               const char       *path,
-               mode_t            mode,
-               fuse_file_info_t *fi)
-{
-  return fs->op.create(path,mode,fi);
-}
-
-int
-fuse_fs_lock(struct fuse_fs   *fs,
-             fuse_file_info_t *fi,
-             int               cmd,
-             struct flock     *lock)
-{
-  return fs->op.lock(fi,cmd,lock);
-}
-
-int
-fuse_fs_flock(struct fuse_fs   *fs,
-              fuse_file_info_t *fi,
-              int               op)
-{
-  return fs->op.flock(fi,op);
-}
-
-int
-fuse_fs_chown(struct fuse_fs *fs,
-              const char     *path,
-              uid_t           uid,
-              gid_t           gid)
-{
-  return fs->op.chown(path,uid,gid);
-}
-
-int
-fuse_fs_fchown(struct fuse_fs         *fs_,
-               const fuse_file_info_t *ffi_,
-               const uid_t             uid_,
-               const gid_t             gid_)
-{
-  return fs_->op.fchown(ffi_,uid_,gid_);
-}
-
-int
-fuse_fs_truncate(struct fuse_fs *fs,
-                 const char     *path,
-                 off_t           size)
-{
-  return fs->op.truncate(path,size);
-}
-
-int
-fuse_fs_ftruncate(struct fuse_fs   *fs,
-                  off_t             size,
-                  fuse_file_info_t *fi)
-{
-  return fs->op.ftruncate(fi,size);
-}
-
-int
-fuse_fs_utimens(struct fuse_fs        *fs,
-                const char            *path,
-                const struct timespec  tv[2])
-{
-  return fs->op.utimens(path,tv);
-}
-
-int
-fuse_fs_futimens(struct fuse_fs         *fs_,
-                 const fuse_file_info_t *ffi_,
-                 const struct timespec   tv_[2])
-{
-  return fs_->op.futimens(ffi_,tv_);
-}
-
-int
-fuse_fs_access(struct fuse_fs *fs,
-               const char     *path,
-               int             mask)
-{
-  return fs->op.access(path,mask);
-}
-
-int
-fuse_fs_readlink(struct fuse_fs *fs,
-                 const char     *path,
-                 char           *buf,
-                 size_t          len)
-{
-  return fs->op.readlink(path,buf,len);
-}
-
-int
-fuse_fs_mknod(struct fuse_fs *fs,
-              const char     *path,
-              mode_t          mode,
-              dev_t           rdev)
-{
-  return fs->op.mknod(path,mode,rdev);
-}
-
-int
-fuse_fs_mkdir(struct fuse_fs *fs,
-              const char     *path,
-              mode_t          mode)
-{
-  return fs->op.mkdir(path,mode);
-}
-
-int
-fuse_fs_setxattr(struct fuse_fs *fs,
-                 const char     *path,
-                 const char     *name,
-		 const char     *value,
-                 size_t          size,
-                 int             flags)
-{
-  return fs->op.setxattr(path,name,value,size,flags);
-}
-
-int
-fuse_fs_getxattr(struct fuse_fs *fs,
-                 const char     *path,
-                 const char     *name,
-                 char           *value,
-                 size_t          size)
-{
-  return fs->op.getxattr(path,name,value,size);
-}
-
-int
-fuse_fs_listxattr(struct fuse_fs *fs,
-                  const char     *path,
-                  char           *list,
-                  size_t          size)
-{
-  return fs->op.listxattr(path,list,size);
-}
-
-int
-fuse_fs_bmap(struct fuse_fs *fs,
-             const char     *path,
-             size_t          blocksize,
-             uint64_t       *idx)
-{
-  return fs->op.bmap(path,blocksize,idx);
-}
-
-int
-fuse_fs_removexattr(struct fuse_fs *fs,
-                    const char     *path,
-                    const char     *name)
-{
-  return fs->op.removexattr(path,name);
-}
-
-int
-fuse_fs_ioctl(struct fuse_fs   *fs,
-              unsigned long     cmd,
-              void             *arg,
-              fuse_file_info_t *fi,
-              unsigned int      flags,
-              void             *data,
-              uint32_t         *out_size)
-{
-  return fs->op.ioctl(fi,cmd,arg,flags,data,out_size);
-}
-
-int
-fuse_fs_poll(struct fuse_fs    *fs,
-             fuse_file_info_t  *fi,
-             fuse_pollhandle_t *ph,
-             unsigned          *reventsp)
-{
-  return fs->op.poll(fi,ph,reventsp);
-}
-
-int
-fuse_fs_fallocate(struct fuse_fs   *fs,
-                  int               mode,
-                  off_t             offset,
-                  off_t             length,
-                  fuse_file_info_t *fi)
-{
-  return fs->op.fallocate(fi,mode,offset,length);
-}
-
-ssize_t
-fuse_fs_copy_file_range(struct fuse_fs   *fs_,
-                        fuse_file_info_t *ffi_in_,
-                        off_t             off_in_,
-                        fuse_file_info_t *ffi_out_,
-                        off_t             off_out_,
-                        size_t            len_,
-                        int               flags_)
-{
-  return fs_->op.copy_file_range(ffi_in_,
-                                 off_in_,
-                                 ffi_out_,
-                                 off_out_,
-                                 len_,
-                                 flags_);
-}
-
 static
 int
 node_open(const struct node *node_)
@@ -1866,8 +1493,8 @@ lookup_path(struct fuse             *f,
   memset(e,0,sizeof(struct fuse_entry_param));
 
   rv = ((fi == NULL) ?
-        fuse_fs_getattr(f->fs,path,&e->attr,&e->timeout) :
-        fuse_fs_fgetattr(f->fs,&e->attr,fi,&e->timeout));
+        f->fs->op.getattr(path,&e->attr,&e->timeout) :
+        f->fs->op.fgetattr(fi,&e->attr,&e->timeout));
 
   if(rv)
     return rv;
@@ -1989,13 +1616,6 @@ reply_entry(fuse_req_t                     req,
     }
 }
 
-void
-fuse_fs_init(struct fuse_fs        *fs,
-             struct fuse_conn_info *conn)
-{
-  fs->op.init(conn);
-}
-
 static
 void
 fuse_lib_init(void                  *data,
@@ -2007,15 +1627,7 @@ fuse_lib_init(void                  *data,
   memset(c,0,sizeof(*c));
   c->ctx.fuse = f;
   conn->want |= FUSE_CAP_EXPORT_SUPPORT;
-  fuse_fs_init(f->fs,conn);
-}
-
-void
-fuse_fs_destroy(struct fuse_fs *fs)
-{
-  if(fs->op.destroy)
-    fs->op.destroy();
-  free(fs);
+  f->fs->op.init(conn);
 }
 
 static
@@ -2027,29 +1639,35 @@ fuse_lib_destroy(void *data)
 
   memset(c,0,sizeof(*c));
   c->ctx.fuse = f;
-  fuse_fs_destroy(f->fs);
+  f->fs->op.destroy();
+  free(f->fs);
   f->fs = NULL;
 }
 
 static
 void
-fuse_lib_lookup(fuse_req_t  req,
-                uint64_t    parent,
-                const char *name)
+fuse_lib_lookup(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
   int err;
+  uint64_t nodeid;
   char *path;
+  const char *name;
+  struct fuse *f;
   struct node *dot = NULL;
-  struct fuse *f = req_fuse_prepare(req);
   struct fuse_entry_param e = {0};
 
+  name   = fuse_hdr_arg(hdr_);
+  nodeid = hdr_->nodeid;
+  f      = req_fuse_prepare(req);
+
   if(name[0] == '.')
     {
       if(name[1] == '\0')
         {
           name = NULL;
           pthread_mutex_lock(&f->lock);
-          dot = get_node_nocheck(f,parent);
+          dot = get_node_nocheck(f,nodeid);
           if(dot == NULL)
             {
               pthread_mutex_unlock(&f->lock);
@@ -2061,7 +1679,7 @@ fuse_lib_lookup(fuse_req_t  req,
         }
       else if((name[1] == '.') && (name[2] == '\0'))
         {
-          if(parent == 1)
+          if(nodeid == 1)
             {
               reply_entry(req,&e,-ENOENT);
               return;
@@ -2069,21 +1687,21 @@ fuse_lib_lookup(fuse_req_t  req,
 
           name = NULL;
           pthread_mutex_lock(&f->lock);
-          parent = get_node(f,parent)->parent->nodeid;
+          nodeid = get_node(f,nodeid)->parent->nodeid;
           pthread_mutex_unlock(&f->lock);
         }
     }
 
-  err = get_path_name(f,parent,name,&path);
+  err = get_path_name(f,nodeid,name,&path);
   if(!err)
     {
-      err = lookup_path(f,parent,name,path,&e,NULL);
+      err = lookup_path(f,nodeid,name,path,&e,NULL);
       if(err == -ENOENT)
         {
           e.ino = 0;
           err = 0;
         }
-      free_path(f,parent,path);
+      free_path(f,nodeid,path);
     }
 
   if(dot)
@@ -2098,34 +1716,37 @@ fuse_lib_lookup(fuse_req_t  req,
 
 static
 void
-do_forget(struct fuse      *f,
-          const uint64_t    ino,
-          const uint64_t    nlookup)
+fuse_lib_forget(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
-  forget_node(f,ino,nlookup);
-}
+  struct fuse *f;
+  struct fuse_forget_in *arg;
+
+  f   = req_fuse(req);
+  arg = fuse_hdr_arg(hdr_);
+
+  forget_node(f,hdr_->nodeid,arg->nlookup);
 
-static
-void
-fuse_lib_forget(fuse_req_t       req,
-                const uint64_t   ino,
-                const uint64_t   nlookup)
-{
-  do_forget(req_fuse(req),ino,nlookup);
   fuse_reply_none(req);
 }
 
 static
 void
-fuse_lib_forget_multi(fuse_req_t               req,
-                      size_t                   count,
-                      struct fuse_forget_data *forgets)
+fuse_lib_forget_multi(fuse_req_t             req,
+                      struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse(req);
-  size_t i;
+  struct fuse *f;
+  struct fuse_batch_forget_in *arg;
+  struct fuse_forget_one      *entry;
+
+  f     = req_fuse(req);
+  arg   = fuse_hdr_arg(hdr_);
+  entry = PARAM(arg);
 
-  for(i = 0; i < count; i++)
-    do_forget(f,forgets[i].ino,forgets[i].nlookup);
+  for(uint32_t i = 0; i < arg->count; i++)
+    forget_node(f,
+                entry[i].nodeid,
+                entry[i].nlookup);
 
   fuse_reply_none(req);
 }
@@ -2133,11 +1754,9 @@ fuse_lib_forget_multi(fuse_req_t               req,
 
 static
 void
-fuse_lib_getattr(fuse_req_t        req,
-                 uint64_t          ino,
-                 fuse_file_info_t *fi)
+fuse_lib_getattr(fuse_req_t             req,
+                 struct fuse_in_header *hdr_)
 {
-
   int err;
   char *path;
   struct fuse *f;
@@ -2145,17 +1764,21 @@ fuse_lib_getattr(fuse_req_t        req,
   struct node *node;
   fuse_timeouts_t timeout;
   fuse_file_info_t ffi = {0};
+  const struct fuse_getattr_in *arg;
 
-  f = req_fuse_prepare(req);
-  if(fi == NULL)
+  arg = fuse_hdr_arg(hdr_);
+  f   = req_fuse_prepare(req);
+
+  if(arg->getattr_flags & FUSE_GETATTR_FH)
+    {
+      ffi.fh = arg->fh;
+    }
+  else
     {
       pthread_mutex_lock(&f->lock);
-      node = get_node(f,ino);
+      node = get_node(f,hdr_->nodeid);
       if(node->is_hidden)
-        {
-          fi = &ffi;
-          fi->fh = node->hidden_fh;
-        }
+        ffi.fh = node->hidden_fh;
       pthread_mutex_unlock(&f->lock);
     }
 
@@ -2163,25 +1786,25 @@ fuse_lib_getattr(fuse_req_t        req,
 
   err = 0;
   path = NULL;
-  if(fi == NULL)
-    err = get_path(f,ino,&path);
+  if(ffi.fh == 0)
+    err = get_path(f,hdr_->nodeid,&path);
 
   if(!err)
     {
-      err = ((fi == NULL) ?
-             fuse_fs_getattr(f->fs,path,&buf,&timeout) :
-             fuse_fs_fgetattr(f->fs,&buf,fi,&timeout));
+      err = ((ffi.fh == 0) ?
+             f->fs->op.getattr(path,&buf,&timeout) :
+             f->fs->op.fgetattr(&ffi,&buf,&timeout));
 
-      free_path(f,ino,path);
+      free_path(f,hdr_->nodeid,path);
     }
 
   if(!err)
     {
       pthread_mutex_lock(&f->lock);
-      node = get_node(f,ino);
+      node = get_node(f,hdr_->nodeid);
       update_stat(node,&buf);
       pthread_mutex_unlock(&f->lock);
-      set_stat(f,ino,&buf);
+      set_stat(f,hdr_->nodeid,&buf);
       fuse_reply_attr(req,&buf,timeout.attr);
     }
   else
@@ -2190,42 +1813,33 @@ fuse_lib_getattr(fuse_req_t        req,
     }
 }
 
-int
-fuse_fs_chmod(struct fuse_fs *fs,
-              const char     *path,
-              mode_t          mode)
-{
-  return fs->op.chmod(path,mode);
-}
-
-int
-fuse_fs_fchmod(struct fuse_fs         *fs_,
-               const fuse_file_info_t *ffi_,
-               const mode_t            mode_)
-{
-  return fs_->op.fchmod(ffi_,mode_);
-}
-
 static
 void
-fuse_lib_setattr(fuse_req_t        req,
-                 uint64_t          ino,
-                 struct stat      *attr,
-                 int               valid,
-                 fuse_file_info_t *fi)
+fuse_lib_setattr(fuse_req_t             req,
+                 struct fuse_in_header *hdr_)
 {
   struct fuse *f = req_fuse_prepare(req);
-  struct stat buf;
+  struct stat stbuf = {0};
   char *path;
   int err;
   struct node *node;
   fuse_timeouts_t timeout;
+  fuse_file_info_t *fi;
   fuse_file_info_t ffi = {0};
+  struct fuse_setattr_in *arg;
 
-  if(fi == NULL)
+  arg = fuse_hdr_arg(hdr_);
+
+  fi = NULL;
+  if(arg->valid & FATTR_FH)
+    {
+      fi = &ffi;
+      fi->fh = arg->fh;
+    }
+  else
     {
       pthread_mutex_lock(&f->lock);
-      node = get_node(f,ino);
+      node = get_node(f,hdr_->nodeid);
       if(node->is_hidden)
         {
           fi = &ffi;
@@ -2234,38 +1848,36 @@ fuse_lib_setattr(fuse_req_t        req,
       pthread_mutex_unlock(&f->lock);
     }
 
-  memset(&buf,0,sizeof(buf));
-
   err = 0;
   path = NULL;
   if(fi == NULL)
-    err = get_path(f,ino,&path);
+    err = get_path(f,hdr_->nodeid,&path);
 
   if(!err)
     {
       err = 0;
-      if(!err && (valid & FATTR_MODE))
+      if(!err && (arg->valid & FATTR_MODE))
         err = ((fi == NULL) ?
-               fuse_fs_chmod(f->fs,path,attr->st_mode) :
-               fuse_fs_fchmod(f->fs,fi,attr->st_mode));
+               f->fs->op.chmod(path,arg->mode) :
+               f->fs->op.fchmod(fi,arg->mode));
 
-      if(!err && (valid & (FATTR_UID | FATTR_GID)))
+      if(!err && (arg->valid & (FATTR_UID | FATTR_GID)))
         {
-          uid_t uid = ((valid & FATTR_UID) ? attr->st_uid : (uid_t)-1);
-          gid_t gid = ((valid & FATTR_GID) ? attr->st_gid : (gid_t)-1);
+          uid_t uid = ((arg->valid & FATTR_UID) ? arg->uid : (uid_t)-1);
+          gid_t gid = ((arg->valid & FATTR_GID) ? arg->gid : (gid_t)-1);
 
           err = ((fi == NULL) ?
-                 fuse_fs_chown(f->fs,path,uid,gid) :
-                 fuse_fs_fchown(f->fs,fi,uid,gid));
+                 f->fs->op.chown(path,uid,gid) :
+                 f->fs->op.fchown(fi,uid,gid));
         }
 
-      if(!err && (valid & FATTR_SIZE))
+      if(!err && (arg->valid & FATTR_SIZE))
         err = ((fi == NULL) ?
-               fuse_fs_truncate(f->fs,path,attr->st_size) :
-               fuse_fs_ftruncate(f->fs,attr->st_size,fi));
+               f->fs->op.truncate(path,arg->size) :
+               f->fs->op.ftruncate(fi,arg->size));
 
 #ifdef HAVE_UTIMENSAT
-      if(!err && (valid & (FATTR_ATIME | FATTR_MTIME)))
+      if(!err && (arg->valid & (FATTR_ATIME | FATTR_MTIME)))
         {
           struct timespec tv[2];
 
@@ -2274,50 +1886,49 @@ fuse_lib_setattr(fuse_req_t        req,
           tv[0].tv_nsec = UTIME_OMIT;
           tv[1].tv_nsec = UTIME_OMIT;
 
-          if(valid & FATTR_ATIME_NOW)
+          if(arg->valid & FATTR_ATIME_NOW)
             tv[0].tv_nsec = UTIME_NOW;
-          else if(valid & FATTR_ATIME)
-            tv[0] = attr->st_atim;
+          else if(arg->valid & FATTR_ATIME)
+            tv[0] = (struct timespec){ arg->atime, arg->atimensec };
 
-          if(valid & FATTR_MTIME_NOW)
+          if(arg->valid & FATTR_MTIME_NOW)
             tv[1].tv_nsec = UTIME_NOW;
-          else if(valid & FATTR_MTIME)
-            tv[1] = attr->st_mtim;
+          else if(arg->valid & FATTR_MTIME)
+            tv[1] = (struct timespec){ arg->mtime, arg->mtimensec };
 
           err = ((fi == NULL) ?
-                 fuse_fs_utimens(f->fs,path,tv) :
-                 fuse_fs_futimens(f->fs,fi,tv));
+                 f->fs->op.utimens(path,tv) :
+                 f->fs->op.futimens(fi,tv));
         }
       else
 #endif
-        if(!err && ((valid & (FATTR_ATIME|FATTR_MTIME)) == (FATTR_ATIME|FATTR_MTIME)))
+        if(!err && ((arg->valid & (FATTR_ATIME|FATTR_MTIME)) == (FATTR_ATIME|FATTR_MTIME)))
           {
             struct timespec tv[2];
-            tv[0].tv_sec = attr->st_atime;
-            tv[0].tv_nsec = ST_ATIM_NSEC(attr);
-            tv[1].tv_sec = attr->st_mtime;
-            tv[1].tv_nsec = ST_MTIM_NSEC(attr);
+            tv[0].tv_sec  = arg->atime;
+            tv[0].tv_nsec = arg->atimensec;
+            tv[1].tv_sec  = arg->mtime;
+            tv[1].tv_nsec = arg->mtimensec;
             err = ((fi == NULL) ?
-                   fuse_fs_utimens(f->fs,path,tv) :
-                   fuse_fs_futimens(f->fs,fi,tv));
+                   f->fs->op.utimens(path,tv) :
+                   f->fs->op.futimens(fi,tv));
           }
 
       if(!err)
         err = ((fi == NULL) ?
-               fuse_fs_getattr(f->fs,path,&buf,&timeout) :
-               fuse_fs_fgetattr(f->fs,&buf,fi,&timeout));
+               f->fs->op.getattr(path,&stbuf,&timeout) :
+               f->fs->op.fgetattr(fi,&stbuf,&timeout));
 
-      free_path(f,ino,path);
+      free_path(f,hdr_->nodeid,path);
     }
 
-
   if(!err)
     {
       pthread_mutex_lock(&f->lock);
-      update_stat(get_node(f,ino),&buf);
+      update_stat(get_node(f,hdr_->nodeid),&stbuf);
       pthread_mutex_unlock(&f->lock);
-      set_stat(f,ino,&buf);
-      fuse_reply_attr(req,&buf,timeout.attr);
+      set_stat(f,hdr_->nodeid,&stbuf);
+      fuse_reply_attr(req,&stbuf,timeout.attr);
     }
   else
     {
@@ -2327,19 +1938,23 @@ fuse_lib_setattr(fuse_req_t        req,
 
 static
 void
-fuse_lib_access(fuse_req_t req,
-                uint64_t   ino,
-                int        mask)
+fuse_lib_access(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  char *path;
   int err;
+  char *path;
+  struct fuse *f;
+  struct fuse_access_in *arg;
 
-  err = get_path(f,ino,&path);
+  arg = fuse_hdr_arg(hdr_);
+
+  f = req_fuse_prepare(req);
+
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_access(f->fs,path,mask);
-      free_path(f,ino,path);
+      err = f->fs->op.access(path,arg->mask);
+      free_path(f,hdr_->nodeid,path);
     }
 
   reply_err(req,err);
@@ -2347,19 +1962,21 @@ fuse_lib_access(fuse_req_t req,
 
 static
 void
-fuse_lib_readlink(fuse_req_t req,
-                  uint64_t   ino)
+fuse_lib_readlink(fuse_req_t             req,
+                  struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  char linkname[PATH_MAX + 1];
-  char *path;
   int err;
+  char *path;
+  struct fuse *f;
+  char linkname[PATH_MAX + 1];
 
-  err = get_path(f,ino,&path);
+  f = req_fuse_prepare(req);
+
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_readlink(f->fs,path,linkname,sizeof(linkname));
-      free_path(f,ino,path);
+      err = f->fs->op.readlink(path,linkname,sizeof(linkname));
+      free_path(f,hdr_->nodeid,path);
     }
 
   if(!err)
@@ -2375,99 +1992,119 @@ fuse_lib_readlink(fuse_req_t req,
 
 static
 void
-fuse_lib_mknod(fuse_req_t  req,
-               uint64_t    parent,
-               const char *name,
-               mode_t      mode,
-               dev_t       rdev)
+fuse_lib_mknod(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  struct fuse_entry_param e;
-  char *path;
   int err;
+  char *path;
+  struct fuse *f;
+  const char* name;
+  struct fuse_entry_param e;
+  struct fuse_mknod_in *arg;
+
+  arg  = fuse_hdr_arg(hdr_);
+  name = PARAM(arg);
+  if(req->f->conn.proto_minor >= 12)
+    req->ctx.umask = arg->umask;
+  else
+    name = (char*)arg + FUSE_COMPAT_MKNOD_IN_SIZE;
+
+  f = req_fuse_prepare(req);
 
-  err = get_path_name(f,parent,name,&path);
+  err = get_path_name(f,hdr_->nodeid,name,&path);
   if(!err)
     {
       err = -ENOSYS;
-      if(S_ISREG(mode))
+      if(S_ISREG(arg->mode))
         {
           fuse_file_info_t fi;
 
           memset(&fi,0,sizeof(fi));
           fi.flags = O_CREAT | O_EXCL | O_WRONLY;
-          err = fuse_fs_create(f->fs,path,mode,&fi);
+          err = f->fs->op.create(path,arg->mode,&fi);
           if(!err)
             {
-              err = lookup_path(f,parent,name,path,&e,
-                                &fi);
-              fuse_fs_release(f->fs,&fi);
+              err = lookup_path(f,hdr_->nodeid,name,path,&e,&fi);
+              f->fs->op.release(&fi);
             }
         }
+
       if(err == -ENOSYS)
         {
-          err = fuse_fs_mknod(f->fs,path,mode,rdev);
+          err = f->fs->op.mknod(path,arg->mode,arg->rdev);
           if(!err)
-            err = lookup_path(f,parent,name,path,&e,NULL);
+            err = lookup_path(f,hdr_->nodeid,name,path,&e,NULL);
         }
-      free_path(f,parent,path);
+
+      free_path(f,hdr_->nodeid,path);
     }
+
   reply_entry(req,&e,err);
 }
 
 static
 void
-fuse_lib_mkdir(fuse_req_t  req,
-               uint64_t    parent,
-               const char *name,
-               mode_t      mode)
+fuse_lib_mkdir(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  struct fuse_entry_param e;
-  char *path;
   int err;
+  char *path;
+  struct fuse *f;
+  const char *name;
+  struct fuse_entry_param e;
+  struct fuse_mkdir_in *arg;
 
-  err = get_path_name(f,parent,name,&path);
+  arg  = fuse_hdr_arg(hdr_);
+  name = PARAM(arg);
+  if(req->f->conn.proto_minor >= 12)
+    req->ctx.umask = arg->umask;
+
+  f = req_fuse_prepare(req);
+
+  err = get_path_name(f,hdr_->nodeid,name,&path);
   if(!err)
     {
-      err = fuse_fs_mkdir(f->fs,path,mode);
+      err = f->fs->op.mkdir(path,arg->mode);
       if(!err)
-        err = lookup_path(f,parent,name,path,&e,NULL);
-      free_path(f,parent,path);
+        err = lookup_path(f,hdr_->nodeid,name,path,&e,NULL);
+      free_path(f,hdr_->nodeid,path);
     }
+
   reply_entry(req,&e,err);
 }
 
 static
 void
-fuse_lib_unlink(fuse_req_t  req,
-                uint64_t    parent,
-                const char *name)
+fuse_lib_unlink(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
   int err;
   char *path;
   struct fuse *f;
+  const char *name;
   struct node *wnode;
 
+  name = PARAM(hdr_);
+
   f = req_fuse_prepare(req);
-  err = get_path_wrlock(f,parent,name,&path,&wnode);
+  err = get_path_wrlock(f,hdr_->nodeid,name,&path,&wnode);
 
   if(!err)
     {
       pthread_mutex_lock(&f->lock);
       if(node_open(wnode))
         {
-          err = fuse_fs_prepare_hide(f->fs,path,&wnode->hidden_fh);
+          err = f->fs->op.prepare_hide(path,&wnode->hidden_fh);
           if(!err)
             wnode->is_hidden = 1;
         }
       pthread_mutex_unlock(&f->lock);
 
-      err = fuse_fs_unlink(f->fs,path);
+      err = f->fs->op.unlink(path);
       if(!err)
-        remove_node(f,parent,name);
+        remove_node(f,hdr_->nodeid,name);
 
-      free_path_wrlock(f,parent,wnode,path);
+      free_path_wrlock(f,hdr_->nodeid,wnode,path);
     }
 
   reply_err(req,err);
@@ -2475,22 +2112,26 @@ fuse_lib_unlink(fuse_req_t  req,
 
 static
 void
-fuse_lib_rmdir(fuse_req_t  req,
-               uint64_t    parent,
-               const char *name)
+fuse_lib_rmdir(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  struct node *wnode;
-  char *path;
   int err;
+  char *path;
+  struct fuse *f;
+  const char *name;
+  struct node *wnode;
+
+  name = PARAM(hdr_);
+
+  f = req_fuse_prepare(req);
 
-  err = get_path_wrlock(f,parent,name,&path,&wnode);
+  err = get_path_wrlock(f,hdr_->nodeid,name,&path,&wnode);
   if(!err)
     {
-      err = fuse_fs_rmdir(f->fs,path);
+      err = f->fs->op.rmdir(path);
       if(!err)
-        remove_node(f,parent,name);
-      free_path_wrlock(f,parent,wnode,path);
+        remove_node(f,hdr_->nodeid,name);
+      free_path_wrlock(f,hdr_->nodeid,wnode,path);
     }
 
   reply_err(req,err);
@@ -2498,25 +2139,28 @@ fuse_lib_rmdir(fuse_req_t  req,
 
 static
 void
-fuse_lib_symlink(fuse_req_t  req_,
-                 const char *linkname_,
-                 uint64_t    parent_,
-                 const char *name_)
+fuse_lib_symlink(fuse_req_t             req_,
+                 struct fuse_in_header *hdr_)
 {
   int rv;
   char *path;
   struct fuse *f;
+  const char *name;
+  const char *linkname;
   struct fuse_entry_param e = {0};
 
+  name     = fuse_hdr_arg(hdr_);
+  linkname = (name + strlen(name) + 1);
+
   f = req_fuse_prepare(req_);
 
-  rv = get_path_name(f,parent_,name_,&path);
+  rv = get_path_name(f,hdr_->nodeid,name,&path);
   if(rv == 0)
     {
-      rv = fuse_fs_symlink(f->fs,linkname_,path,&e.attr,&e.timeout);
+      rv = f->fs->op.symlink(linkname,path,&e.attr,&e.timeout);
       if(rv == 0)
-        rv = set_path_info(f,parent_,name_,&e);
-      free_path(f,parent_,path);
+        rv = set_path_info(f,hdr_->nodeid,name,&e);
+      free_path(f,hdr_->nodeid,path);
     }
 
   reply_entry(req_,&e,rv);
@@ -2524,21 +2168,25 @@ fuse_lib_symlink(fuse_req_t  req_,
 
 static
 void
-fuse_lib_rename(fuse_req_t  req,
-                uint64_t    olddir,
-                const char *oldname,
-                uint64_t    newdir,
-                const char *newname)
+fuse_lib_rename(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
   int err;
   struct fuse *f;
   char *oldpath;
   char *newpath;
+  const char *oldname;
+  const char *newname;
   struct node *wnode1;
   struct node *wnode2;
+  struct fuse_rename_in *arg;
+
+  arg     = fuse_hdr_arg(hdr_);
+  oldname = PARAM(arg);
+  newname = (oldname + strlen(oldname) + 1);
 
   f = req_fuse_prepare(req);
-  err = get_path2(f,olddir,oldname,newdir,newname,
+  err = get_path2(f,hdr_->nodeid,oldname,arg->newdir,newname,
                   &oldpath,&newpath,&wnode1,&wnode2);
 
   if(!err)
@@ -2546,17 +2194,17 @@ fuse_lib_rename(fuse_req_t  req,
       pthread_mutex_lock(&f->lock);
       if(node_open(wnode2))
         {
-          err = fuse_fs_prepare_hide(f->fs,newpath,&wnode2->hidden_fh);
+          err = f->fs->op.prepare_hide(newpath,&wnode2->hidden_fh);
           if(!err)
             wnode2->is_hidden = 1;
         }
       pthread_mutex_unlock(&f->lock);
 
-      err = fuse_fs_rename(f->fs,oldpath,newpath);
+      err = f->fs->op.rename(oldpath,newpath);
       if(!err)
-        err = rename_node(f,olddir,oldname,newdir,newname);
+        err = rename_node(f,hdr_->nodeid,oldname,arg->newdir,newname);
 
-      free_path2(f,olddir,newdir,wnode1,wnode2,oldpath,newpath);
+      free_path2(f,hdr_->nodeid,arg->newdir,wnode1,wnode2,oldpath,newpath);
     }
 
   reply_err(req,err);
@@ -2564,27 +2212,32 @@ fuse_lib_rename(fuse_req_t  req,
 
 static
 void
-fuse_lib_link(fuse_req_t  req,
-              uint64_t    ino,
-              uint64_t    newparent,
-              const char *newname)
+fuse_lib_link(fuse_req_t             req,
+              struct fuse_in_header *hdr_)
 {
   int rv;
   char *oldpath;
   char *newpath;
   struct fuse *f;
+  const char *newname;
+  struct fuse_link_in *arg;
   struct fuse_entry_param e = {0};
 
+  arg = fuse_hdr_arg(hdr_);
+  newname = PARAM(arg);
+
   f = req_fuse_prepare(req);
 
-  rv = get_path2(f,ino,NULL,newparent,newname,
+  rv = get_path2(f,
+                 arg->oldnodeid,NULL,
+                 hdr_->nodeid,newname,
                  &oldpath,&newpath,NULL,NULL);
   if(!rv)
     {
-      rv = fuse_fs_link(f->fs,oldpath,newpath,&e.attr,&e.timeout);
+      rv = f->fs->op.link(oldpath,newpath,&e.attr,&e.timeout);
       if(rv == 0)
-        rv = set_path_info(f,newparent,newname,&e);
-      free_path2(f,ino,newparent,NULL,NULL,oldpath,newpath);
+        rv = set_path_info(f,hdr_->nodeid,newname,&e);
+      free_path2(f,arg->oldnodeid,hdr_->nodeid,NULL,NULL,oldpath,newpath);
     }
 
   reply_entry(req,&e,rv);
@@ -2601,7 +2254,7 @@ fuse_do_release(struct fuse      *f,
   int was_hidden;
 
   fh = 0;
-  fuse_fs_release(f->fs,fi);
+  f->fs->op.release(fi);
 
   pthread_mutex_lock(&f->lock);
   node = get_node(f,ino);
@@ -2617,38 +2270,49 @@ fuse_do_release(struct fuse      *f,
   pthread_mutex_unlock(&f->lock);
 
   if(was_hidden)
-    fuse_fs_free_hide(f->fs,fh);
+    f->fs->op.free_hide(fh);
 }
 
 static
 void
-fuse_lib_create(fuse_req_t        req,
-                uint64_t          parent,
-                const char       *name,
-                mode_t            mode,
-                fuse_file_info_t *fi)
+fuse_lib_create(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
   int err;
   char *path;
   struct fuse *f;
+  const char *name;
+  fuse_file_info_t ffi = {0};
   struct fuse_entry_param e;
+  struct fuse_create_in *arg;
+
+  arg  = fuse_hdr_arg(hdr_);
+  name = PARAM(arg);
+
+  ffi.flags = arg->flags;
+
+  if(req->f->conn.proto_minor >= 12)
+    req->ctx.umask = arg->umask;
+  else
+    name = (char*)arg + sizeof(struct fuse_open_in);
 
   f = req_fuse_prepare(req);
-  err = get_path_name(f,parent,name,&path);
+
+  err = get_path_name(f,hdr_->nodeid,name,&path);
   if(!err)
     {
-      err = fuse_fs_create(f->fs,path,mode,fi);
+      err = f->fs->op.create(path,arg->mode,&ffi);
       if(!err)
         {
-          err = lookup_path(f,parent,name,path,&e,fi);
+          err = lookup_path(f,hdr_->nodeid,name,path,&e,&ffi);
           if(err)
             {
-              fuse_fs_release(f->fs,fi);
+              f->fs->op.release(&ffi);
             }
           else if(!S_ISREG(e.attr.st_mode))
             {
               err = -EIO;
-              fuse_fs_release(f->fs,fi);
+              f->fs->op.release(&ffi);
               forget_node(f,e.ino,1);
             }
         }
@@ -2660,11 +2324,11 @@ fuse_lib_create(fuse_req_t        req,
       get_node(f,e.ino)->open_count++;
       pthread_mutex_unlock(&f->lock);
 
-      if(fuse_reply_create(req,&e,fi) == -ENOENT)
+      if(fuse_reply_create(req,&e,&ffi) == -ENOENT)
         {
           /* The open syscall was interrupted,so it
              must be cancelled */
-          fuse_do_release(f,e.ino,fi);
+          fuse_do_release(f,e.ino,&ffi);
           forget_node(f,e.ino,1);
         }
     }
@@ -2673,7 +2337,7 @@ fuse_lib_create(fuse_req_t        req,
       reply_err(req,err);
     }
 
-  free_path(f,parent,path);
+  free_path(f,hdr_->nodeid,path);
 }
 
 static
@@ -2695,7 +2359,7 @@ open_auto_cache(struct fuse      *f,
       struct stat stbuf;
 
       pthread_mutex_unlock(&f->lock);
-      err = fuse_fs_fgetattr(f->fs,&stbuf,fi,&timeout);
+      err = f->fs->op.fgetattr(fi,&stbuf,&timeout);
       pthread_mutex_lock(&f->lock);
 
       if(!err)
@@ -2714,58 +2378,73 @@ open_auto_cache(struct fuse      *f,
 
 static
 void
-fuse_lib_open(fuse_req_t        req,
-              uint64_t          ino,
-              fuse_file_info_t *fi)
+fuse_lib_open(fuse_req_t             req,
+              struct fuse_in_header *hdr_)
 {
   int err;
   char *path;
   struct fuse *f;
+  fuse_file_info_t ffi = {0};
+  struct fuse_open_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+
+  ffi.flags = arg->flags;
 
   f = req_fuse_prepare(req);
-  err = get_path(f,ino,&path);
+
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_open(f->fs,path,fi);
+      err = f->fs->op.open(path,&ffi);
       if(!err)
         {
-          if(fi && fi->auto_cache)
-            open_auto_cache(f,ino,path,fi);
+          if(ffi.auto_cache)
+            open_auto_cache(f,hdr_->nodeid,path,&ffi);
         }
     }
 
   if(!err)
     {
       pthread_mutex_lock(&f->lock);
-      get_node(f,ino)->open_count++;
+      get_node(f,hdr_->nodeid)->open_count++;
       pthread_mutex_unlock(&f->lock);
       /* The open syscall was interrupted,so it must be cancelled */
-      if(fuse_reply_open(req,fi) == -ENOENT)
-        fuse_do_release(f,ino,fi);
+      if(fuse_reply_open(req,&ffi) == -ENOENT)
+        fuse_do_release(f,hdr_->nodeid,&ffi);
     }
   else
     {
       reply_err(req,err);
     }
 
-  free_path(f,ino,path);
+  free_path(f,hdr_->nodeid,path);
 }
 
 static
 void
-fuse_lib_read(fuse_req_t        req,
-              uint64_t          ino,
-              size_t            size,
-              off_t             off,
-              fuse_file_info_t *fi)
+fuse_lib_read(fuse_req_t             req,
+              struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  struct fuse_bufvec *buf = NULL;
   int res;
+  struct fuse *f;
+  fuse_file_info_t ffi = {0};
+  struct fuse_bufvec *buf = NULL;
+  struct fuse_read_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh = arg->fh;
+  if(req->f->conn.proto_minor >= 9)
+    {
+      ffi.flags      = arg->flags;
+      ffi.lock_owner = arg->lock_owner;
+    }
 
-  res = fuse_fs_read_buf(f->fs,&buf,size,off,fi);
+  f = req_fuse_prepare(req);
+
+  res = f->fs->op.read_buf(&ffi,&buf,arg->size,arg->offset);
 
-  if(res == 0)
+  if(res >= 0)
     fuse_reply_data(req,buf,FUSE_BUF_SPLICE_MOVE);
   else
     reply_err(req,res);
@@ -2775,17 +2454,33 @@ fuse_lib_read(fuse_req_t        req,
 
 static
 void
-fuse_lib_write_buf(fuse_req_t          req,
-                   uint64_t            ino,
-                   struct fuse_bufvec *buf,
-                   off_t               off,
-                   fuse_file_info_t   *fi)
+fuse_lib_write(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
   int res;
-  struct fuse *f = req_fuse_prepare(req);
+  char *data;
+  struct fuse *f;
+  fuse_file_info_t ffi = {0};
+  struct fuse_write_in *arg;
 
-  res = fuse_fs_write_buf(f->fs,buf,off,fi);
-  free_path(f,ino,NULL);
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh = arg->fh;
+  ffi.writepage = !!(arg->write_flags & 1);
+  if(req->f->conn.proto_minor < 9)
+    {
+      data = ((char*)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
+    }
+  else
+    {
+      ffi.flags = arg->flags;
+      ffi.lock_owner = arg->lock_owner;
+      data = PARAM(arg);
+    }
+
+  f = req_fuse_prepare(req);
+
+  res = f->fs->op.write(&ffi,data,arg->size,arg->offset);
+  free_path(f,hdr_->nodeid,NULL);
 
   if(res >= 0)
     fuse_reply_write(req,res);
@@ -2795,15 +2490,21 @@ fuse_lib_write_buf(fuse_req_t          req,
 
 static
 void
-fuse_lib_fsync(fuse_req_t        req,
-               uint64_t          ino,
-               int               datasync,
-               fuse_file_info_t *fi)
+fuse_lib_fsync(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
   int err;
-  struct fuse *f = req_fuse_prepare(req);
+  struct fuse *f;
+  struct fuse_fsync_in *arg;
+  fuse_file_info_t ffi = {0};
 
-  err = fuse_fs_fsync(f->fs,datasync,fi);
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh = arg->fh;
+
+  f = req_fuse_prepare(req);
+
+  err = f->fs->op.fsync(&ffi,
+                        !!(arg->fsync_flags & 1));
 
   reply_err(req,err);
 }
@@ -2821,15 +2522,21 @@ get_dirhandle(const fuse_file_info_t *llfi,
 
 static
 void
-fuse_lib_opendir(fuse_req_t        req,
-                 uint64_t          ino,
-                 fuse_file_info_t *llfi)
+fuse_lib_opendir(fuse_req_t             req,
+                 struct fuse_in_header *hdr_)
 {
   int err;
   char *path;
   struct fuse_dh *dh;
-  fuse_file_info_t fi;
-  struct fuse *f = req_fuse_prepare(req);
+  fuse_file_info_t llffi = {0};
+  fuse_file_info_t ffi = {0};
+  struct fuse *f;
+  struct fuse_open_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  llffi.flags = arg->flags;
+
+  f = req_fuse_prepare(req);
 
   dh = (struct fuse_dh *)calloc(1,sizeof(struct fuse_dh));
   if(dh == NULL)
@@ -2841,27 +2548,25 @@ fuse_lib_opendir(fuse_req_t        req,
   fuse_dirents_init(&dh->d);
   fuse_mutex_init(&dh->lock);
 
-  llfi->fh = (uintptr_t)dh;
-
-  memset(&fi,0,sizeof(fi));
-  fi.flags = llfi->flags;
+  llffi.fh = (uintptr_t)dh;
+  ffi.flags = llffi.flags;
 
-  err = get_path(f,ino,&path);
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_opendir(f->fs,path,&fi);
-      dh->fh = fi.fh;
-      llfi->keep_cache    = fi.keep_cache;
-      llfi->cache_readdir = fi.cache_readdir;
+      err = f->fs->op.opendir(path,&ffi);
+      dh->fh = ffi.fh;
+      llffi.keep_cache    = ffi.keep_cache;
+      llffi.cache_readdir = ffi.cache_readdir;
     }
 
   if(!err)
     {
-      if(fuse_reply_open(req,llfi) == -ENOENT)
+      if(fuse_reply_open(req,&llffi) == -ENOENT)
         {
           /* The opendir syscall was interrupted,so it
              must be cancelled */
-          fuse_fs_releasedir(f->fs,&fi);
+          f->fs->op.releasedir(&ffi);
           pthread_mutex_destroy(&dh->lock);
           free(dh);
         }
@@ -2872,7 +2577,8 @@ fuse_lib_opendir(fuse_req_t        req,
       pthread_mutex_destroy(&dh->lock);
       free(dh);
     }
-  free_path(f,ino,path);
+
+  free_path(f,hdr_->nodeid,path);
 }
 
 static
@@ -2902,27 +2608,31 @@ readdir_buf(fuse_dirents_t *d_,
 
 static
 void
-fuse_lib_readdir(fuse_req_t        req_,
-                 uint64_t          ino_,
-                 size_t            size_,
-                 off_t             off_,
-                 fuse_file_info_t *llffi_)
+fuse_lib_readdir(fuse_req_t             req_,
+                 struct fuse_in_header *hdr_)
 {
   int rv;
+  size_t size;
   struct fuse *f;
   fuse_dirents_t *d;
   struct fuse_dh *dh;
-  fuse_file_info_t fi;
+  fuse_file_info_t ffi = {0};
+  fuse_file_info_t llffi = {0};
+  struct fuse_read_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  size = arg->size;
+  llffi.fh = arg->fh;
 
   f  = req_fuse_prepare(req_);
-  dh = get_dirhandle(llffi_,&fi);
+  dh = get_dirhandle(&llffi,&ffi);
   d  = &dh->d;
 
   pthread_mutex_lock(&dh->lock);
 
   rv = 0;
-  if((off_ == 0) || (kv_size(d->data) == 0))
-    rv = fuse_fs_readdir(f->fs,&fi,d);
+  if((arg->offset == 0) || (kv_size(d->data) == 0))
+    rv = f->fs->op.readdir(&ffi,d);
 
   if(rv)
     {
@@ -2930,11 +2640,11 @@ fuse_lib_readdir(fuse_req_t        req_,
       goto out;
     }
 
-  size_ = readdir_buf_size(d,size_,off_);
+  size = readdir_buf_size(d,size,arg->offset);
 
   fuse_reply_buf(req_,
-                 readdir_buf(d,off_),
-                 size_);
+                 readdir_buf(d,arg->offset),
+                 size);
 
  out:
   pthread_mutex_unlock(&dh->lock);
@@ -2942,27 +2652,31 @@ fuse_lib_readdir(fuse_req_t        req_,
 
 static
 void
-fuse_lib_readdir_plus(fuse_req_t        req_,
-                      uint64_t          ino_,
-                      size_t            size_,
-                      off_t             off_,
-                      fuse_file_info_t *llffi_)
+fuse_lib_readdir_plus(fuse_req_t             req_,
+                      struct fuse_in_header *hdr_)
 {
   int rv;
+  size_t size;
   struct fuse *f;
   fuse_dirents_t *d;
   struct fuse_dh *dh;
-  fuse_file_info_t fi;
+  fuse_file_info_t ffi = {0};
+  fuse_file_info_t llffi = {0};
+  struct fuse_read_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  size = arg->size;
+  llffi.fh = arg->fh;
 
   f  = req_fuse_prepare(req_);
-  dh = get_dirhandle(llffi_,&fi);
+  dh = get_dirhandle(&llffi,&ffi);
   d  = &dh->d;
 
   pthread_mutex_lock(&dh->lock);
 
   rv = 0;
-  if((off_ == 0) || (kv_size(d->data) == 0))
-    rv = fuse_fs_readdir_plus(f->fs,&fi,d);
+  if((arg->offset == 0) || (kv_size(d->data) == 0))
+    rv = f->fs->op.readdir_plus(&ffi,d);
 
   if(rv)
     {
@@ -2970,11 +2684,11 @@ fuse_lib_readdir_plus(fuse_req_t        req_,
       goto out;
     }
 
-  size_ = readdir_buf_size(d,size_,off_);
+  size = readdir_buf_size(d,size,arg->offset);
 
   fuse_reply_buf(req_,
-                 readdir_buf(d,off_),
-                 size_);
+                 readdir_buf(d,arg->offset),
+                 size);
 
  out:
   pthread_mutex_unlock(&dh->lock);
@@ -2982,18 +2696,23 @@ fuse_lib_readdir_plus(fuse_req_t        req_,
 
 static
 void
-fuse_lib_releasedir(fuse_req_t        req_,
-                    uint64_t          ino_,
-                    fuse_file_info_t *llfi_)
+fuse_lib_releasedir(fuse_req_t             req_,
+                    struct fuse_in_header *hdr_)
 {
   struct fuse *f;
   struct fuse_dh *dh;
-  fuse_file_info_t fi;
+  fuse_file_info_t ffi;
+  fuse_file_info_t llffi = {0};
+  struct fuse_release_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  llffi.fh    = arg->fh;
+  llffi.flags = arg->flags;
 
   f  = req_fuse_prepare(req_);
-  dh = get_dirhandle(llfi_,&fi);
+  dh = get_dirhandle(&llffi,&ffi);
 
-  fuse_fs_releasedir(f->fs,&fi);
+  f->fs->op.releasedir(&ffi);
 
   /* Done to keep race condition between last readdir reply and the unlock */
   pthread_mutex_lock(&dh->lock);
@@ -3006,40 +2725,47 @@ fuse_lib_releasedir(fuse_req_t        req_,
 
 static
 void
-fuse_lib_fsyncdir(fuse_req_t        req,
-                  uint64_t          ino,
-                  int               datasync,
-                  fuse_file_info_t *llfi)
+fuse_lib_fsyncdir(fuse_req_t             req,
+                  struct fuse_in_header *hdr_)
 {
   int err;
-  fuse_file_info_t fi;
-  struct fuse *f = req_fuse_prepare(req);
+  struct fuse *f;
+  fuse_file_info_t ffi;
+  fuse_file_info_t llffi = {0};
+  struct fuse_fsync_in *arg;
 
-  get_dirhandle(llfi,&fi);
+  arg = fuse_hdr_arg(hdr_);
+  llffi.fh = arg->fh;
+
+  f = req_fuse_prepare(req);
 
-  err = fuse_fs_fsyncdir(f->fs,datasync,&fi);
+  get_dirhandle(&llffi,&ffi);
+
+  err = f->fs->op.fsyncdir(&ffi,
+                           !!(arg->fsync_flags & FUSE_FSYNC_FDATASYNC));
 
   reply_err(req,err);
 }
 
 static
 void
-fuse_lib_statfs(fuse_req_t req,
-                uint64_t   ino)
+fuse_lib_statfs(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  struct statvfs buf;
-  char *path = NULL;
   int err = 0;
+  char *path = NULL;
+  struct fuse *f;
+  struct statvfs buf = {0};
 
-  memset(&buf,0,sizeof(buf));
-  if(ino)
-    err = get_path(f,ino,&path);
+  f = req_fuse_prepare(req);
+
+  if(hdr_->nodeid)
+    err = get_path(f,hdr_->nodeid,&path);
 
   if(!err)
     {
-      err = fuse_fs_statfs(f->fs,path ? path : "/",&buf);
-      free_path(f,ino,path);
+      err = f->fs->op.statfs(path ? path : "/",&buf);
+      free_path(f,hdr_->nodeid,path);
     }
 
   if(!err)
@@ -3050,22 +2776,31 @@ fuse_lib_statfs(fuse_req_t req,
 
 static
 void
-fuse_lib_setxattr(fuse_req_t  req,
-                  uint64_t    ino,
-                  const char *name,
-                  const char *value,
-                  size_t      size,
-                  int         flags)
+fuse_lib_setxattr(fuse_req_t             req,
+                  struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  char *path;
   int err;
+  char *path;
+  const char *name;
+  const char *value;
+  struct fuse *f;
+  struct fuse_setxattr_in *arg;
 
-  err = get_path(f,ino,&path);
+  arg   = fuse_hdr_arg(hdr_);
+  if((req->f->conn.capable & FUSE_SETXATTR_EXT) && (req->f->conn.want & FUSE_SETXATTR_EXT))
+    name = PARAM(arg);
+  else
+    name = (((char*)arg) + FUSE_COMPAT_SETXATTR_IN_SIZE);
+
+  value = (name + strlen(name) + 1);
+
+  f = req_fuse_prepare(req);
+
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_setxattr(f->fs,path,name,value,size,flags);
-      free_path(f,ino,path);
+      err = f->fs->op.setxattr(path,name,value,arg->size,arg->flags);
+      free_path(f,hdr_->nodeid,path);
     }
 
   reply_err(req,err);
@@ -3086,7 +2821,7 @@ common_getxattr(struct fuse *f,
   err = get_path(f,ino,&path);
   if(!err)
     {
-      err = fuse_fs_getxattr(f->fs,path,name,value,size);
+      err = f->fs->op.getxattr(path,name,value,size);
 
       free_path(f,ino,path);
     }
@@ -3096,24 +2831,29 @@ common_getxattr(struct fuse *f,
 
 static
 void
-fuse_lib_getxattr(fuse_req_t  req,
-                  uint64_t    ino,
-                  const char *name,
-                  size_t      size)
+fuse_lib_getxattr(fuse_req_t             req,
+                  struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
   int res;
+  struct fuse *f;
+  const char* name;
+  struct fuse_getxattr_in *arg;
+
+  arg  = fuse_hdr_arg(hdr_);
+  name = PARAM(arg);
 
-  if(size)
+  f = req_fuse_prepare(req);
+
+  if(arg->size)
     {
-      char *value = (char *)malloc(size);
+      char *value = (char*)malloc(arg->size);
       if(value == NULL)
         {
           reply_err(req,-ENOMEM);
           return;
         }
 
-      res = common_getxattr(f,req,ino,name,value,size);
+      res = common_getxattr(f,req,hdr_->nodeid,name,value,arg->size);
       if(res > 0)
         fuse_reply_buf(req,value,res);
       else
@@ -3122,7 +2862,7 @@ fuse_lib_getxattr(fuse_req_t  req,
     }
   else
     {
-      res = common_getxattr(f,req,ino,name,NULL,0);
+      res = common_getxattr(f,req,hdr_->nodeid,name,NULL,0);
       if(res >= 0)
         fuse_reply_xattr(req,res);
       else
@@ -3144,7 +2884,7 @@ common_listxattr(struct fuse *f,
   err = get_path(f,ino,&path);
   if(!err)
     {
-      err = fuse_fs_listxattr(f->fs,path,list,size);
+      err = f->fs->op.listxattr(path,list,size);
       free_path(f,ino,path);
     }
 
@@ -3153,23 +2893,27 @@ common_listxattr(struct fuse *f,
 
 static
 void
-fuse_lib_listxattr(fuse_req_t req,
-                   uint64_t   ino,
-                   size_t     size)
+fuse_lib_listxattr(fuse_req_t             req,
+                   struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
   int res;
+  struct fuse *f;
+  struct fuse_getxattr_in *arg;
 
-  if(size)
+  arg = fuse_hdr_arg(hdr_);
+
+  f = req_fuse_prepare(req);
+
+  if(arg->size)
     {
-      char *list = (char *)malloc(size);
+      char *list = (char*)malloc(arg->size);
       if(list == NULL)
         {
           reply_err(req,-ENOMEM);
           return;
         }
 
-      res = common_listxattr(f,req,ino,list,size);
+      res = common_listxattr(f,req,hdr_->nodeid,list,arg->size);
       if(res > 0)
         fuse_reply_buf(req,list,res);
       else
@@ -3178,7 +2922,7 @@ fuse_lib_listxattr(fuse_req_t req,
     }
   else
     {
-      res = common_listxattr(f,req,ino,NULL,0);
+      res = common_listxattr(f,req,hdr_->nodeid,NULL,0);
       if(res >= 0)
         fuse_reply_xattr(req,res);
       else
@@ -3188,19 +2932,23 @@ fuse_lib_listxattr(fuse_req_t req,
 
 static
 void
-fuse_lib_removexattr(fuse_req_t  req,
-                     uint64_t    ino,
-                     const char *name)
+fuse_lib_removexattr(fuse_req_t                   req,
+                     const struct fuse_in_header *hdr_)
 {
-  struct fuse *f = req_fuse_prepare(req);
-  char *path;
   int err;
+  char *path;
+  const char *name;
+  struct fuse *f;
 
-  err = get_path(f,ino,&path);
+  name = fuse_hdr_arg(hdr_);
+
+  f = req_fuse_prepare(req);
+
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_removexattr(f->fs,path,name);
-      free_path(f,ino,path);
+      err = f->fs->op.removexattr(path,name);
+      free_path(f,hdr_->nodeid,path);
     }
 
   reply_err(req,err);
@@ -3208,28 +2956,27 @@ fuse_lib_removexattr(fuse_req_t  req,
 
 static
 void
-fuse_lib_copy_file_range(fuse_req_t        req_,
-                         uint64_t          nodeid_in_,
-                         off_t             off_in_,
-                         fuse_file_info_t *ffi_in_,
-                         uint64_t          nodeid_out_,
-                         off_t             off_out_,
-                         fuse_file_info_t *ffi_out_,
-                         size_t            len_,
-                         int               flags_)
+fuse_lib_copy_file_range(fuse_req_t                   req_,
+                         const struct fuse_in_header *hdr_)
 {
   ssize_t rv;
   struct fuse *f;
+  fuse_file_info_t ffi_in = {0};
+  fuse_file_info_t ffi_out = {0};
+  const struct fuse_copy_file_range_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  ffi_in.fh  = arg->fh_in;
+  ffi_out.fh = arg->fh_out;
 
   f = req_fuse_prepare(req_);
 
-  rv = fuse_fs_copy_file_range(f->fs,
-                               ffi_in_,
-                               off_in_,
-                               ffi_out_,
-                               off_out_,
-                               len_,
-                               flags_);
+  rv = f->fs->op.copy_file_range(&ffi_in,
+                                 arg->off_in,
+                                 &ffi_out,
+                                 arg->off_out,
+                                 arg->len,
+                                 arg->flags);
 
   if(rv >= 0)
     fuse_reply_write(req_,rv);
@@ -3394,8 +3141,8 @@ fuse_flush_common(struct fuse      *f,
   memset(&lock,0,sizeof(lock));
   lock.l_type = F_UNLCK;
   lock.l_whence = SEEK_SET;
-  err = fuse_fs_flush(f->fs,fi);
-  errlock = fuse_fs_lock(f->fs,fi,F_SETLK,&lock);
+  err = f->fs->op.flush(fi);
+  errlock = f->fs->op.lock(fi,F_SETLK,&lock);
 
   if(errlock != -ENOSYS)
     {
@@ -3416,35 +3163,62 @@ fuse_flush_common(struct fuse      *f,
 
 static
 void
-fuse_lib_release(fuse_req_t        req,
-                 uint64_t          ino,
-                 fuse_file_info_t *fi)
+fuse_lib_release(fuse_req_t             req,
+                 struct fuse_in_header *hdr_)
 {
   int err = 0;
-  struct fuse *f = req_fuse_prepare(req);
+  struct fuse *f;
+  fuse_file_info_t ffi = {0};
+  struct fuse_release_in *arg;
 
-  if(fi->flush)
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh    = arg->fh;
+  ffi.flags = arg->flags;
+  if(req->f->conn.proto_minor >= 8)
+    {
+      ffi.flush      = !!(arg->release_flags & FUSE_RELEASE_FLUSH);
+      ffi.lock_owner = arg->lock_owner;
+    }
+  else
     {
-      err = fuse_flush_common(f,req,ino,fi);
+      ffi.flock_release = 1;
+      ffi.lock_owner    = arg->lock_owner;
+    }
+
+  f = req_fuse_prepare(req);
+
+  if(ffi.flush)
+    {
+      err = fuse_flush_common(f,req,hdr_->nodeid,&ffi);
       if(err == -ENOSYS)
         err = 0;
     }
 
-  fuse_do_release(f,ino,fi);
+  fuse_do_release(f,hdr_->nodeid,&ffi);
 
   reply_err(req,err);
 }
 
 static
 void
-fuse_lib_flush(fuse_req_t        req,
-               uint64_t          ino,
-               fuse_file_info_t *fi)
+fuse_lib_flush(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
   int err;
-  struct fuse *f = req_fuse_prepare(req);
+  struct fuse *f;
+  fuse_file_info_t ffi = {0};
+  struct fuse_flush_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+
+  ffi.fh = arg->fh;
+  ffi.flush = 1;
+  if(req->f->conn.proto_minor >= 7)
+    ffi.lock_owner = arg->lock_owner;
+
+  f = req_fuse_prepare(req);
 
-  err = fuse_flush_common(f,req,ino,fi);
+  err = fuse_flush_common(f,req,hdr_->nodeid,&ffi);
 
   reply_err(req,err);
 }
@@ -3460,37 +3234,62 @@ fuse_lock_common(fuse_req_t        req,
   int err;
   struct fuse *f = req_fuse_prepare(req);
 
-  err = fuse_fs_lock(f->fs,fi,cmd,lock);
+  err = f->fs->op.lock(fi,cmd,lock);
 
   return err;
 }
 
 static
 void
-fuse_lib_getlk(fuse_req_t        req,
-               uint64_t          ino,
-               fuse_file_info_t *fi,
-               struct flock     *lock)
+convert_fuse_file_lock(const struct fuse_file_lock *fl,
+                       struct flock                *flock)
+{
+  memset(flock, 0, sizeof(struct flock));
+  flock->l_type = fl->type;
+  flock->l_whence = SEEK_SET;
+  flock->l_start = fl->start;
+  if (fl->end == OFFSET_MAX)
+    flock->l_len = 0;
+  else
+    flock->l_len = fl->end - fl->start + 1;
+  flock->l_pid = fl->pid;
+}
+
+static
+void
+fuse_lib_getlk(fuse_req_t                   req,
+               const struct fuse_in_header *hdr_)
 {
   int err;
-  struct lock l;
+  struct fuse *f;
+  struct lock lk;
+  struct flock flk;
   struct lock *conflict;
-  struct fuse *f = req_fuse(req);
+  fuse_file_info_t ffi = {0};
+  const struct fuse_lk_in *arg;
 
-  flock_to_lock(lock,&l);
-  l.owner = fi->lock_owner;
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh         = arg->fh;
+  ffi.lock_owner = arg->owner;
+
+  convert_fuse_file_lock(&arg->lk,&flk);
+
+  f = req_fuse(req);
+
+  flock_to_lock(&flk,&lk);
+  lk.owner = ffi.lock_owner;
   pthread_mutex_lock(&f->lock);
-  conflict = locks_conflict(get_node(f,ino),&l);
+  conflict = locks_conflict(get_node(f,hdr_->nodeid),&lk);
   if(conflict)
-    lock_to_flock(conflict,lock);
+    lock_to_flock(conflict,&flk);
   pthread_mutex_unlock(&f->lock);
   if(!conflict)
-    err = fuse_lock_common(req,ino,fi,lock,F_GETLK);
+    err = fuse_lock_common(req,hdr_->nodeid,&ffi,&flk,F_GETLK);
   else
     err = 0;
 
   if(!err)
-    fuse_reply_lock(req,lock);
+    fuse_reply_lock(req,&flk);
   else
     reply_err(req,err);
 }
@@ -3529,78 +3328,101 @@ fuse_lib_flock(fuse_req_t        req,
   int err;
   struct fuse *f = req_fuse_prepare(req);
 
-  err = fuse_fs_flock(f->fs,fi,op);
+  err = f->fs->op.flock(fi,op);
 
   reply_err(req,err);
 }
 
 static
 void
-fuse_lib_bmap(fuse_req_t req,
-              uint64_t   ino,
-              size_t     blocksize,
-              uint64_t   idx)
+fuse_lib_bmap(fuse_req_t                   req,
+              const struct fuse_in_header *hdr_)
 {
   int err;
   char *path;
-  struct fuse *f = req_fuse_prepare(req);
+  struct fuse *f;
+  uint64_t block;
+  const struct fuse_bmap_in *arg;
 
-  err = get_path(f,ino,&path);
+  arg = fuse_hdr_arg(hdr_);
+  block = arg->block;
+
+  f = req_fuse_prepare(req);
+
+  err = get_path(f,hdr_->nodeid,&path);
   if(!err)
     {
-      err = fuse_fs_bmap(f->fs,path,blocksize,&idx);
-      free_path(f,ino,path);
+      err = f->fs->op.bmap(path,arg->blocksize,&block);
+      free_path(f,hdr_->nodeid,path);
     }
 
   if(!err)
-    fuse_reply_bmap(req,idx);
+    fuse_reply_bmap(req,block);
   else
     reply_err(req,err);
 }
 
 static
 void
-fuse_lib_ioctl(fuse_req_t        req,
-               uint64_t          ino,
-               unsigned long     cmd,
-               void             *arg,
-               fuse_file_info_t *llfi,
-               unsigned int      flags,
-               const void       *in_buf,
-               uint32_t          in_bufsz,
-               uint32_t          out_bufsz_)
+fuse_lib_ioctl(fuse_req_t                   req,
+               const struct fuse_in_header *hdr_)
 {
   int err;
   char *out_buf = NULL;
   struct fuse *f = req_fuse_prepare(req);
-  fuse_file_info_t fi;
-  uint32_t out_bufsz = out_bufsz_;
+  fuse_file_info_t ffi;
+  fuse_file_info_t llffi = {0};
+  const void *in_buf;
+  uint32_t out_size;
+  const struct fuse_ioctl_in *arg;
+
+  arg = fuse_hdr_arg(hdr_);
+  if((arg->flags & FUSE_IOCTL_DIR) && !(req->f->conn.want & FUSE_CAP_IOCTL_DIR))
+    {
+      fuse_reply_err(req,ENOTTY);
+      return;
+    }
+
+  if((sizeof(void*) == 4)             &&
+     (req->f->conn.proto_minor >= 16) &&
+     !(arg->flags & FUSE_IOCTL_32BIT))
+    {
+      req->ioctl_64bit = 1;
+    }
+
+  llffi.fh = arg->fh;
+  out_size = arg->out_size;
+  in_buf   = (arg->in_size ? PARAM(arg) : NULL);
 
   err = -EPERM;
-  if(flags & FUSE_IOCTL_UNRESTRICTED)
+  if(arg->flags & FUSE_IOCTL_UNRESTRICTED)
     goto err;
 
-  if(flags & FUSE_IOCTL_DIR)
-    get_dirhandle(llfi,&fi);
+  if(arg->flags & FUSE_IOCTL_DIR)
+    get_dirhandle(&llffi,&ffi);
   else
-    fi = *llfi;
+    ffi = llffi;
 
-  if(out_bufsz)
+  if(out_size)
     {
       err = -ENOMEM;
-      out_buf = malloc(out_bufsz);
+      out_buf = malloc(out_size);
       if(!out_buf)
         goto err;
     }
 
-  assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
+  assert(!arg->in_size || !out_size || arg->in_size == out_size);
   if(out_buf)
-    memcpy(out_buf,in_buf,in_bufsz);
+    memcpy(out_buf,in_buf,arg->in_size);
 
-  err = fuse_fs_ioctl(f->fs,cmd,arg,&fi,flags,
-                      out_buf ?: (void *)in_buf,&out_bufsz);
+  err = f->fs->op.ioctl(&ffi,
+                        arg->cmd,
+                        (void*)(uintptr_t)arg->arg,
+                        arg->flags,
+                        out_buf ?: (void *)in_buf,
+                        &out_size);
 
-  fuse_reply_ioctl(req,err,out_buf,out_bufsz);
+  fuse_reply_ioctl(req,err,out_buf,out_size);
   goto out;
  err:
   reply_err(req,err);
@@ -3610,16 +3432,34 @@ fuse_lib_ioctl(fuse_req_t        req,
 
 static
 void
-fuse_lib_poll(fuse_req_t         req,
-              uint64_t           ino,
-              fuse_file_info_t  *fi,
-              fuse_pollhandle_t *ph)
+fuse_lib_poll(fuse_req_t                   req,
+              const struct fuse_in_header *hdr_)
 {
   int err;
   struct fuse *f = req_fuse_prepare(req);
   unsigned revents = 0;
+  fuse_file_info_t ffi = {0};
+  fuse_pollhandle_t *ph = NULL;
+  const struct fuse_poll_in *arg;
 
-  err = fuse_fs_poll(f->fs,fi,ph,&revents);
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh = arg->fh;
+
+  if(arg->flags & FUSE_POLL_SCHEDULE_NOTIFY)
+    {
+      ph = (fuse_pollhandle_t*)malloc(sizeof(fuse_pollhandle_t));
+      if(ph == NULL)
+        {
+          fuse_reply_err(req,ENOMEM);
+          return;
+        }
+
+      ph->kh = arg->kh;
+      ph->ch = req->ch;
+      ph->f  = req->f;
+    }
+
+  err = f->fs->op.poll(&ffi,ph,&revents);
 
   if(!err)
     fuse_reply_poll(req,revents);
@@ -3629,17 +3469,23 @@ fuse_lib_poll(fuse_req_t         req,
 
 static
 void
-fuse_lib_fallocate(fuse_req_t        req,
-                   uint64_t          ino,
-                   int               mode,
-                   off_t             offset,
-                   off_t             length,
-                   fuse_file_info_t *fi)
+fuse_lib_fallocate(fuse_req_t                   req,
+                   const struct fuse_in_header *hdr_)
 {
   int err;
-  struct fuse *f = req_fuse_prepare(req);
+  struct fuse *f;
+  fuse_file_info_t ffi = {0};
+  const struct fuse_fallocate_in *arg;
 
-  err = fuse_fs_fallocate(f->fs,mode,offset,length,fi);
+  arg = fuse_hdr_arg(hdr_);
+  ffi.fh = arg->fh;
+
+  f = req_fuse_prepare(req);
+
+  err = f->fs->op.fallocate(&ffi,
+                            arg->mode,
+                            arg->offset,
+                            arg->length);
 
   reply_err(req,err);
 }
@@ -3800,7 +3646,7 @@ static struct fuse_lowlevel_ops fuse_path_ops =
    .statfs          = fuse_lib_statfs,
    .symlink         = fuse_lib_symlink,
    .unlink          = fuse_lib_unlink,
-   .write_buf       = fuse_lib_write_buf,
+   .write           = fuse_lib_write,
   };
 
 int
@@ -3809,22 +3655,6 @@ fuse_notify_poll(fuse_pollhandle_t *ph)
   return fuse_lowlevel_notify_poll(ph);
 }
 
-static
-void
-free_cmd(struct fuse_cmd *cmd)
-{
-  free(cmd->buf);
-  free(cmd);
-}
-
-void
-fuse_process_cmd(struct fuse     *f,
-                 struct fuse_cmd *cmd)
-{
-  fuse_session_process(f->se,cmd->buf,cmd->buflen,cmd->ch);
-  free_cmd(cmd);
-}
-
 int
 fuse_exited(struct fuse *f)
 {
@@ -3837,57 +3667,10 @@ fuse_get_session(struct fuse *f)
   return f->se;
 }
 
-static
-struct fuse_cmd*
-fuse_alloc_cmd(size_t bufsize)
-{
-  struct fuse_cmd *cmd = (struct fuse_cmd *)malloc(sizeof(*cmd));
-
-  if(cmd == NULL)
-    {
-      fprintf(stderr,"fuse: failed to allocate cmd\n");
-      return NULL;
-    }
-
-  cmd->buf = (char *)malloc(bufsize);
-  if(cmd->buf == NULL)
-    {
-      fprintf(stderr,"fuse: failed to allocate read buffer\n");
-      free(cmd);
-      return NULL;
-    }
-
-  return cmd;
-}
-
-struct fuse_cmd*
-fuse_read_cmd(struct fuse *f)
-{
-  struct fuse_chan *ch = fuse_session_next_chan(f->se,NULL);
-  size_t bufsize = fuse_chan_bufsize(ch);
-  struct fuse_cmd *cmd = fuse_alloc_cmd(bufsize);
-
-  if(cmd != NULL)
-    {
-      int res = fuse_chan_recv(&ch,cmd->buf,bufsize);
-      if(res <= 0)
-        {
-          free_cmd(cmd);
-          if(res < 0 && res != -EINTR && res != -EAGAIN)
-            fuse_exit(f);
-          return NULL;
-        }
-      cmd->buflen = res;
-      cmd->ch = ch;
-    }
-
-  return cmd;
-}
-
 void
 fuse_exit(struct fuse *f)
 {
-  fuse_session_exit(f->se);
+  f->se->exited = 1;
 }
 
 struct fuse_context*
@@ -3913,14 +3696,15 @@ static const struct fuse_opt fuse_lib_opts[] =
    FUSE_LIB_OPT("nogc",               nogc,1),
    FUSE_LIB_OPT("umask=",	      set_mode,1),
    FUSE_LIB_OPT("umask=%o",	      umask,0),
-   FUSE_LIB_OPT("uid=",	      set_uid,1),
+   FUSE_LIB_OPT("uid=",	              set_uid,1),
    FUSE_LIB_OPT("uid=%d",	      uid,0),
-   FUSE_LIB_OPT("gid=",	      set_gid,1),
+   FUSE_LIB_OPT("gid=",	              set_gid,1),
    FUSE_LIB_OPT("gid=%d",	      gid,0),
-   FUSE_LIB_OPT("noforget",          remember,-1),
-   FUSE_LIB_OPT("remember=%u",       remember,0),
-   FUSE_LIB_OPT("threads=%d",        threads,0),
-   FUSE_LIB_OPT("use_ino",           use_ino,1),
+   FUSE_LIB_OPT("noforget",           remember,-1),
+   FUSE_LIB_OPT("remember=%u",        remember,0),
+   FUSE_LIB_OPT("threads=%d",         read_thread_count,0),
+   FUSE_LIB_OPT("read-thread-count=%d", read_thread_count,0),
+   FUSE_LIB_OPT("process-thread-count=%d", process_thread_count,-1),
    FUSE_OPT_END
   };
 
@@ -4014,32 +3798,32 @@ metrics_log_nodes_info(struct fuse *f_,
 
   lfmp_lock(&f_->node_fmp);
   snprintf(buf,sizeof(buf),
-           "time: %zu\n"
-           "sizeof(node): %zu\n"
-           "node id_table size: %zu\n"
-           "node id_table usage: %zu\n"
-           "node id_table total allocated memory: %zu\n"
-           "node name_table size: %zu\n"
-           "node name_table usage: %zu\n"
-           "node name_table total allocated memory: %zu\n"
-           "node memory pool slab count: %zu\n"
+           "time: %"PRIu64"\n"
+           "sizeof(node): %"PRIu64"\n"
+           "node id_table size: %"PRIu64"\n"
+           "node id_table usage: %"PRIu64"\n"
+           "node id_table total allocated memory: %"PRIu64"\n"
+           "node name_table size: %"PRIu64"\n"
+           "node name_table usage: %"PRIu64"\n"
+           "node name_table total allocated memory: %"PRIu64"\n"
+           "node memory pool slab count: %"PRIu64"\n"
            "node memory pool usage ratio: %f\n"
-           "node memory pool avail objs: %zu\n"
-           "node memory pool total allocated memory: %zu\n"
+           "node memory pool avail objs: %"PRIu64"\n"
+           "node memory pool total allocated memory: %"PRIu64"\n"
            "\n"
            ,
-           time(NULL),
-           sizeof(struct node),
-           f_->id_table.size,
-           f_->id_table.use,
-           (f_->id_table.size * sizeof(struct node*)),
-           f_->name_table.size,
-           f_->name_table.use,
-           (f_->name_table.size * sizeof(struct node*)),
-           fmp_slab_count(&f_->node_fmp.fmp),
+           (uint64_t)time(NULL),
+           (uint64_t)sizeof(struct node),
+           (uint64_t)f_->id_table.size,
+           (uint64_t)f_->id_table.use,
+           (uint64_t)(f_->id_table.size * sizeof(struct node*)),
+           (uint64_t)f_->name_table.size,
+           (uint64_t)f_->name_table.use,
+           (uint64_t)(f_->name_table.size * sizeof(struct node*)),
+           (uint64_t)fmp_slab_count(&f_->node_fmp.fmp),
            fmp_slab_usage_ratio(&f_->node_fmp.fmp),
-           fmp_avail_objs(&f_->node_fmp.fmp),
-           fmp_total_allocated_memory(&f_->node_fmp.fmp)
+           (uint64_t)fmp_avail_objs(&f_->node_fmp.fmp),
+           (uint64_t)fmp_total_allocated_memory(&f_->node_fmp.fmp)
            );
   lfmp_unlock(&f_->node_fmp);
 
@@ -4212,7 +3996,7 @@ fuse_new_common(struct fuse_chan             *ch,
   /* Horrible compatibility hack to stop the destructor from being
      called on the filesystem without init being called first */
   fs->op.destroy = NULL;
-  fuse_fs_destroy(f->fs);
+  free(f->fs);
  out_free:
   free(f);
  out_delete_context_key:
@@ -4249,7 +4033,7 @@ fuse_destroy(struct fuse *f)
           for(node = f->id_table.array[i]; node != NULL; node = node->id_next)
             {
               if(node->is_hidden)
-                fuse_fs_free_hide(f->fs,node->hidden_fh);
+                f->fs->op.free_hide(node->hidden_fh);
             }
         }
     }
@@ -4278,9 +4062,15 @@ fuse_destroy(struct fuse *f)
 }
 
 int
-fuse_config_num_threads(const struct fuse *fuse_)
+fuse_config_read_thread_count(const struct fuse *f_)
+{
+  return f_->conf.read_thread_count;
+}
+
+int
+fuse_config_process_thread_count(const struct fuse *f_)
 {
-  return fuse_->conf.threads;
+  return f_->conf.process_thread_count;
 }
 
 void
diff --git a/libfuse/lib/fuse_i.h b/libfuse/lib/fuse_i.h
index 096d52d..af3d899 100644
--- a/libfuse/lib/fuse_i.h
+++ b/libfuse/lib/fuse_i.h
@@ -10,24 +10,25 @@
 
 #include "fuse.h"
 #include "fuse_lowlevel.h"
+#include "fuse_msgbuf.h"
+
+#include "extern_c.h"
 
 struct fuse_chan;
 struct fuse_ll;
 
 struct fuse_session
 {
-  struct fuse_session_ops op;
-
-  int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
-                     struct fuse_chan **chp);
+  int (*receive_buf)(struct fuse_session *se,
+                     fuse_msgbuf_t       *msgbuf);
 
-  void (*process_buf)(void *data, const struct fuse_buf *buf,
-                      struct fuse_chan *ch);
+  void (*process_buf)(struct fuse_session *se,
+                      const fuse_msgbuf_t *msgbuf);
 
-  void *data;
+  void (*destroy)(void *data);
 
+  struct fuse_ll *f;
   volatile int exited;
-
   struct fuse_chan *ch;
 };
 
@@ -46,8 +47,7 @@ struct fuse_notify_req
   void (*reply)(struct fuse_notify_req *,
                 fuse_req_t,
                 uint64_t,
-                const void *,
-                const struct fuse_buf *);
+                const void *);
   struct fuse_notify_req *next;
   struct fuse_notify_req *prev;
 };
@@ -65,11 +65,11 @@ struct fuse_ll
   int no_splice_move;
   int no_splice_read;
   struct fuse_lowlevel_ops op;
-  int got_init;
   void *userdata;
   uid_t owner;
   struct fuse_conn_info conn;
   pthread_mutex_t lock;
+  int got_init;
   int got_destroy;
   pthread_key_t pipe_key;
   int broken_splice_nonblock;
@@ -84,6 +84,8 @@ struct fuse_cmd
   struct fuse_chan *ch;
 };
 
+EXTERN_C_BEGIN
+
 struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
 			     const struct fuse_operations *op,
 			     size_t op_size);
@@ -111,3 +113,5 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
 			       int *fd);
 
 int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
+
+EXTERN_C_END
diff --git a/libfuse/lib/fuse_kern_chan.c b/libfuse/lib/fuse_kern_chan.c
deleted file mode 100644
index 8bfe8ac..0000000
--- a/libfuse/lib/fuse_kern_chan.c
+++ /dev/null
@@ -1,103 +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 LGPLv2.
-  See the file COPYING.LIB
-*/
-
-#include "fuse_lowlevel.h"
-#include "fuse_kernel.h"
-#include "fuse_i.h"
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <assert.h>
-
-static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
-				  size_t size)
-{
-  struct fuse_chan *ch = *chp;
-  int err;
-  ssize_t res;
-  struct fuse_session *se = fuse_chan_session(ch);
-  assert(se != NULL);
-
- restart:
-  res = read(fuse_chan_fd(ch), buf, size);
-  err = errno;
-
-  if (fuse_session_exited(se))
-    return 0;
-  if (res == -1) {
-    /* ENOENT means the operation was interrupted, it's safe
-       to restart */
-    if (err == ENOENT)
-      goto restart;
-
-    if (err == ENODEV) {
-      fuse_session_exit(se);
-      return 0;
-    }
-    /* Errors occurring during normal operation: EINTR (read
-       interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
-       umounted) */
-    if (err != EINTR && err != EAGAIN)
-      perror("fuse: reading device");
-    return -err;
-  }
-  if ((size_t) res < sizeof(struct fuse_in_header)) {
-    fprintf(stderr, "short read on fuse device\n");
-    return -EIO;
-  }
-  return res;
-}
-
-static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
-			       size_t count)
-{
-  if (iov) {
-    ssize_t res = writev(fuse_chan_fd(ch), iov, count);
-    int err = errno;
-
-    if (res == -1) {
-      struct fuse_session *se = fuse_chan_session(ch);
-
-      assert(se != NULL);
-
-      /* ENOENT means the operation was interrupted */
-      if (!fuse_session_exited(se) && err != ENOENT)
-        perror("fuse: writing device");
-      return -err;
-    }
-  }
-  return 0;
-}
-
-static void fuse_kern_chan_destroy(struct fuse_chan *ch)
-{
-  int fd = fuse_chan_fd(ch);
-
-  if (fd != -1)
-    close(fd);
-}
-
-struct fuse_chan *
-fuse_kern_chan_new(int fd_)
-{
-  long pagesize;
-  size_t bufsize;
-  struct fuse_chan_ops op =
-    {
-      .receive = fuse_kern_chan_receive,
-      .send    = fuse_kern_chan_send,
-      .destroy = fuse_kern_chan_destroy,
-    };
-
-  pagesize = sysconf(_SC_PAGESIZE);
-
-  bufsize = ((FUSE_MAX_MAX_PAGES * pagesize) + 0x1000);
-
-  return fuse_chan_new(&op, fd_, bufsize, NULL);
-}
diff --git a/libfuse/lib/fuse_ll.hpp b/libfuse/lib/fuse_ll.hpp
new file mode 100644
index 0000000..965243c
--- /dev/null
+++ b/libfuse/lib/fuse_ll.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "fuse_msgbuf.h"
+
+int fuse_receive_buf(struct fuse_session *se,
+                     fuse_msgbuf_t       *msgbuf);
+void fuse_process_buf(void *data,
+                      const fuse_msgbuf_t *msgbuf,
+                      struct fuse_chan *ch);
+
+int fuse_receive_buf_splice(struct fuse_chan *ch,
+                            fuse_msgbuf_t    *msgbuf);
+void fuse_process_buf_splice(struct fuse_chan    *ch,
+                             const fuse_msgbuf_t *msgbuf,
+                             void                *data);
diff --git a/libfuse/lib/fuse_loop_mt.c b/libfuse/lib/fuse_loop_mt.c
deleted file mode 100644
index 00a0ee9..0000000
--- a/libfuse/lib/fuse_loop_mt.c
+++ /dev/null
@@ -1,232 +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 LGPLv2.
-  See the file COPYING.LIB.
-*/
-
-#include "fuse_i.h"
-#include "fuse_kernel.h"
-#include "fuse_lowlevel.h"
-#include "fuse_misc.h"
-
-#include <errno.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <unistd.h>
-
-/* Environment var controlling the thread stack size */
-#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
-
-struct fuse_worker
-{
-  struct fuse_worker *prev;
-  struct fuse_worker *next;
-  pthread_t thread_id;
-  size_t bufsize;
-  char *buf;
-  struct fuse_mt *mt;
-};
-
-struct fuse_mt
-{
-  struct fuse_session *se;
-  struct fuse_chan *prevch;
-  struct fuse_worker main;
-  sem_t finish;
-  int exit;
-  int error;
-};
-
-static
-void
-list_add_worker(struct fuse_worker *w,
-                struct fuse_worker *next)
-{
-  struct fuse_worker *prev = next->prev;
-  w->next = next;
-  w->prev = prev;
-  prev->next = w;
-  next->prev = w;
-}
-
-static void list_del_worker(struct fuse_worker *w)
-{
-  struct fuse_worker *prev = w->prev;
-  struct fuse_worker *next = w->next;
-  prev->next = next;
-  next->prev = prev;
-}
-
-static int fuse_loop_start_thread(struct fuse_mt *mt);
-
-static
-void*
-fuse_do_work(void *data)
-{
-  struct fuse_worker *w  = (struct fuse_worker *) data;
-  struct fuse_mt     *mt = w->mt;
-
-  while(!fuse_session_exited(mt->se))
-    {
-      int res;
-      struct fuse_buf fbuf;
-      struct fuse_chan *ch = mt->prevch;
-
-      fbuf = (struct fuse_buf){ .mem  = w->buf,
-                                .size = w->bufsize };
-
-      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
-      res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
-      pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
-      if(res == -EINTR)
-        continue;
-      if(res <= 0) {
-        if(res < 0) {
-          fuse_session_exit(mt->se);
-          mt->error = -1;
-        }
-        break;
-      }
-
-      if(mt->exit)
-        return NULL;
-
-      fuse_session_process_buf(mt->se, &fbuf, ch);
-    }
-
-  sem_post(&mt->finish);
-
-  return NULL;
-}
-
-int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
-{
-  sigset_t oldset;
-  sigset_t newset;
-  int res;
-  pthread_attr_t attr;
-  char *stack_size;
-
-  /* Override default stack size */
-  pthread_attr_init(&attr);
-  stack_size = getenv(ENVNAME_THREAD_STACK);
-  if(stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
-    fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
-
-  /* Disallow signal reception in worker threads */
-  sigfillset(&newset);
-  pthread_sigmask(SIG_BLOCK,&newset,&oldset);
-  res = pthread_create(thread_id, &attr, func, arg);
-  pthread_sigmask(SIG_SETMASK,&oldset,NULL);
-  pthread_attr_destroy(&attr);
-  if(res != 0) {
-    fprintf(stderr, "fuse: error creating thread: %s\n",
-            strerror(res));
-    return -1;
-  }
-
-  return 0;
-}
-
-static int fuse_loop_start_thread(struct fuse_mt *mt)
-{
-  int res;
-  struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
-  if(!w) {
-    fprintf(stderr, "fuse: failed to allocate worker structure\n");
-    return -1;
-  }
-  memset(w, 0, sizeof(struct fuse_worker));
-  w->bufsize = fuse_chan_bufsize(mt->prevch);
-  w->buf = calloc(w->bufsize,1);
-  w->mt = mt;
-  if(!w->buf) {
-    fprintf(stderr, "fuse: failed to allocate read buffer\n");
-    free(w);
-    return -1;
-  }
-
-  res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
-  if(res == -1) {
-    free(w->buf);
-    free(w);
-    return -1;
-  }
-  list_add_worker(w, &mt->main);
-
-  return 0;
-}
-
-static void fuse_join_worker(struct fuse_worker *w)
-{
-  pthread_join(w->thread_id, NULL);
-  list_del_worker(w);
-  free(w->buf);
-  free(w);
-}
-
-static int number_of_threads(void)
-{
-#ifdef _SC_NPROCESSORS_ONLN
-  return sysconf(_SC_NPROCESSORS_ONLN);
-#endif
-
-  return 4;
-}
-
-int
-fuse_session_loop_mt(struct fuse_session *se_,
-                     const int            threads_)
-{
-  int i;
-  int err;
-  int threads;
-  struct fuse_mt mt;
-  struct fuse_worker *w;
-
-  memset(&mt,0,sizeof(struct fuse_mt));
-  mt.se = se_;
-  mt.prevch = fuse_session_next_chan(se_,NULL);
-  mt.error = 0;
-  mt.main.thread_id = pthread_self();
-  mt.main.prev = mt.main.next = &mt.main;
-  sem_init(&mt.finish,0,0);
-
-  threads = ((threads_ > 0) ? threads_ : number_of_threads());
-  if(threads_ < 0)
-    threads /= -threads_;
-  if(threads == 0)
-    threads = 1;
-
-  err = 0;
-  for(i = 0; (i < threads) && !err; i++)
-    err = fuse_loop_start_thread(&mt);
-
-  if(!err)
-    {
-      /* sem_wait() is interruptible */
-      while(!fuse_session_exited(se_))
-        sem_wait(&mt.finish);
-
-      for(w = mt.main.next; w != &mt.main; w = w->next)
-        pthread_cancel(w->thread_id);
-      mt.exit = 1;
-
-      while(mt.main.next != &mt.main)
-        fuse_join_worker(mt.main.next);
-
-      err = mt.error;
-    }
-
-  sem_destroy(&mt.finish);
-  fuse_session_reset(se_);
-
-  return err;
-}
diff --git a/libfuse/lib/fuse_loop_mt.cpp b/libfuse/lib/fuse_loop_mt.cpp
new file mode 100644
index 0000000..c1c9b93
--- /dev/null
+++ b/libfuse/lib/fuse_loop_mt.cpp
@@ -0,0 +1,266 @@
+#include "thread_pool.hpp"
+
+#include "fuse_i.h"
+#include "fuse_kernel.h"
+#include "fuse_lowlevel.h"
+#include "fuse_misc.h"
+
+#include "fuse_msgbuf.hpp"
+#include "fuse_ll.hpp"
+
+#include <errno.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <cassert>
+#include <vector>
+
+
+struct fuse_worker_data_t
+{
+  struct fuse_session *se;
+  sem_t finished;
+  std::function<void(fuse_worker_data_t*,fuse_msgbuf_t*)> msgbuf_processor;
+  std::function<fuse_msgbuf_t*(void)> msgbuf_allocator;
+  std::shared_ptr<ThreadPool> tp;
+};
+
+class WorkerCleanup
+{
+public:
+  WorkerCleanup(fuse_worker_data_t *wd_)
+    : _wd(wd_)
+  {
+  }
+
+  ~WorkerCleanup()
+  {
+    fuse_session_exit(_wd->se);
+    sem_post(&_wd->finished);
+  }
+
+private:
+  fuse_worker_data_t *_wd;
+};
+
+static
+bool
+retriable_receive_error(const int err_)
+{
+  switch(err_)
+    {
+    case -EINTR:
+    case -EAGAIN:
+    case -ENOENT:
+      return true;
+    default:
+      return false;
+    }
+}
+
+static
+bool
+fatal_receive_error(const int err_)
+{
+  return (err_ < 0);
+}
+
+static
+void*
+handle_receive_error(const int      rv_,
+                     fuse_msgbuf_t *msgbuf_)
+{
+  msgbuf_free(msgbuf_);
+
+  fprintf(stderr,
+          "mergerfs: error reading from /dev/fuse - %s (%d)\n",
+          strerror(-rv_),
+          -rv_);
+
+  return NULL;
+}
+
+static
+void*
+fuse_do_work(void *data)
+{
+  fuse_worker_data_t *wd = (fuse_worker_data_t*)data;
+  fuse_session       *se = wd->se;
+  auto               &process_msgbuf = wd->msgbuf_processor;
+  auto               &msgbuf_allocator = wd->msgbuf_allocator;
+  WorkerCleanup       workercleanup(wd);
+
+  while(!fuse_session_exited(se))
+    {
+      int rv;
+      fuse_msgbuf_t *msgbuf;
+
+      msgbuf = msgbuf_allocator();
+
+      do
+        {
+          pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
+          rv = se->receive_buf(se,msgbuf);
+          pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
+          if(rv == 0)
+            return NULL;
+          if(retriable_receive_error(rv))
+            continue;
+          if(fatal_receive_error(rv))
+            return handle_receive_error(rv,msgbuf);
+        } while(false);
+
+      process_msgbuf(wd,msgbuf);
+    }
+
+  return NULL;
+}
+
+int
+fuse_start_thread(pthread_t *thread_id,
+                  void      *(*func)(void *),
+                  void      *arg)
+{
+  int res;
+  sigset_t oldset;
+  sigset_t newset;
+
+  sigfillset(&newset);
+  pthread_sigmask(SIG_BLOCK,&newset,&oldset);
+  res = pthread_create(thread_id,NULL,func,arg);
+  pthread_sigmask(SIG_SETMASK,&oldset,NULL);
+
+  if(res != 0)
+    {
+      fprintf(stderr,
+              "fuse: error creating thread: %s\n",
+              strerror(res));
+      return -1;
+    }
+
+  return 0;
+}
+
+static
+int
+calculate_thread_count(const int raw_thread_count_)
+{
+  int thread_count;
+
+  thread_count = 4;
+  if(raw_thread_count_ == 0)
+    thread_count = std::thread::hardware_concurrency();
+  else if(raw_thread_count_ < 0)
+    thread_count = (std::thread::hardware_concurrency() / -raw_thread_count_);
+  else if(raw_thread_count_ > 0)
+    thread_count = raw_thread_count_;
+
+  if(thread_count <= 0)
+    thread_count = 1;
+
+  return thread_count;
+}
+
+static
+void
+calculate_thread_counts(int *read_thread_count_,
+                        int *process_thread_count_)
+{
+  if((*read_thread_count_ == -1) && (*process_thread_count_ == -1))
+    {
+      int nproc;
+
+      nproc = std::thread::hardware_concurrency();
+      *read_thread_count_ = 2;
+      *process_thread_count_ = std::max(2,(nproc - 2));
+    }
+  else
+    {
+      *read_thread_count_ = ::calculate_thread_count(*read_thread_count_);
+      if(*process_thread_count_ != -1)
+        *process_thread_count_ = ::calculate_thread_count(*process_thread_count_);
+    }
+}
+
+static
+void
+process_msgbuf_sync(fuse_worker_data_t *wd_,
+                    fuse_msgbuf_t      *msgbuf_)
+{
+  wd_->se->process_buf(wd_->se,msgbuf_);
+  msgbuf_free(msgbuf_);
+}
+
+static
+void
+process_msgbuf_async(fuse_worker_data_t *wd_,
+                     fuse_msgbuf_t      *msgbuf_)
+{
+  const auto func = [=] {
+    process_msgbuf_sync(wd_,msgbuf_);
+  };
+
+  wd_->tp->enqueue_work(func);
+}
+
+int
+fuse_session_loop_mt(struct fuse_session *se_,
+                     const int            raw_read_thread_count_,
+                     const int            raw_process_thread_count_)
+{
+  int err;
+  int read_thread_count;
+  int process_thread_count;
+  fuse_worker_data_t wd = {0};
+  std::vector<pthread_t> threads;
+
+  read_thread_count    = raw_read_thread_count_;
+  process_thread_count = raw_process_thread_count_;
+  ::calculate_thread_counts(&read_thread_count,&process_thread_count);
+
+  if(process_thread_count > 0)
+    {
+      wd.tp = std::make_shared<ThreadPool>(process_thread_count);
+      wd.msgbuf_processor = process_msgbuf_async;
+    }
+  else
+    {
+      wd.msgbuf_processor = process_msgbuf_sync;
+    }
+
+  wd.msgbuf_allocator = ((se_->f->splice_read) ? msgbuf_alloc : msgbuf_alloc_memonly);
+
+  wd.se = se_;
+  sem_init(&wd.finished,0,0);
+
+  err = 0;
+  for(int i = 0; i < read_thread_count; i++)
+    {
+      pthread_t thread_id;
+      err = fuse_start_thread(&thread_id,fuse_do_work,&wd);
+      assert(err == 0);
+      threads.push_back(thread_id);
+    }
+
+  if(!err)
+    {
+      /* sem_wait() is interruptible */
+      while(!fuse_session_exited(se_))
+        sem_wait(&wd.finished);
+
+      for(const auto &thread_id : threads)
+        pthread_cancel(thread_id);
+
+      for(const auto &thread_id : threads)
+        pthread_join(thread_id,NULL);
+    }
+
+  sem_destroy(&wd.finished);
+
+  return err;
+}
diff --git a/libfuse/lib/fuse_lowlevel.c b/libfuse/lib/fuse_lowlevel.c
index 91998ae..04b0f90 100644
--- a/libfuse/lib/fuse_lowlevel.c
+++ b/libfuse/lib/fuse_lowlevel.c
@@ -16,6 +16,8 @@
 #include "fuse_kernel.h"
 #include "fuse_opt.h"
 #include "fuse_misc.h"
+#include "fuse_pollhandle.h"
+#include "fuse_msgbuf.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -35,19 +37,12 @@
 #endif
 
 
-#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
+#define PARAM(inarg) (((char*)(inarg)) + sizeof(*(inarg)))
 #define OFFSET_MAX 0x7fffffffffffffffLL
 
 #define container_of(ptr, type, member) ({                      \
-      const typeof( ((type *)0)->member ) *__mptr = (ptr);      \
-      (type *)( (char *)__mptr - offsetof(type,member) );})
-
-struct fuse_pollhandle_t
-{
-  uint64_t kh;
-  struct fuse_chan *ch;
-  struct fuse_ll *f;
-};
+      const typeof( ((type*)0)->member ) *__mptr = (ptr);       \
+      (type *)( (char*)__mptr - offsetof(type,member) );})
 
 static size_t pagesize;
 static lfmp_t g_FMP_fuse_req;
@@ -92,23 +87,6 @@ convert_stat(const struct stat *stbuf_,
   attr_->ctimensec = ST_CTIM_NSEC(stbuf_);
 }
 
-static
-void
-convert_attr(const struct fuse_setattr_in *attr_,
-             struct stat                  *stbuf_)
-{
-  stbuf_->st_mode  = attr_->mode;
-  stbuf_->st_uid   = attr_->uid;
-  stbuf_->st_gid   = attr_->gid;
-  stbuf_->st_size  = attr_->size;
-  stbuf_->st_atime = attr_->atime;
-  stbuf_->st_mtime = attr_->mtime;
-  stbuf_->st_ctime = attr_->ctime;
-  ST_ATIM_NSEC_SET(stbuf_,attr_->atimensec);
-  ST_MTIM_NSEC_SET(stbuf_,attr_->mtimensec);
-  ST_CTIM_NSEC_SET(stbuf_,attr_->ctimensec);
-}
-
 static
 size_t
 iov_length(const struct iovec *iov,
@@ -136,7 +114,7 @@ fuse_ll_alloc_req(struct fuse_ll *f)
   struct fuse_req *req;
 
   req = (struct fuse_req*)lfmp_calloc(&g_FMP_fuse_req);
-  if (req == NULL)
+  if(req == NULL)
     {
       fprintf(stderr, "fuse: failed to allocate request\n");
     }
@@ -156,11 +134,16 @@ fuse_send_msg(struct fuse_ll   *f,
               struct iovec     *iov,
               int               count)
 {
+  int rv;
   struct fuse_out_header *out = iov[0].iov_base;
 
   out->len = iov_length(iov, count);
 
-  return fuse_chan_send(ch, iov, count);
+  rv = writev(fuse_chan_fd(ch),iov,count);
+  if(rv == -1)
+    return -errno;
+
+  return 0;
 }
 
 int
@@ -171,7 +154,7 @@ fuse_send_reply_iov_nofree(fuse_req_t    req,
 {
   struct fuse_out_header out;
 
-  if (error <= -1000 || error > 0)
+  if(error <= -1000 || error > 0)
     {
       fprintf(stderr, "fuse: bad error value: %i\n",error);
       error = -ERANGE;
@@ -210,7 +193,7 @@ send_reply(fuse_req_t  req,
 {
   struct iovec iov[2];
   int count = 1;
-  if (argsize)
+  if(argsize)
     {
       iov[1].iov_base = (void *) arg;
       iov[1].iov_len = argsize;
@@ -254,8 +237,6 @@ fuse_reply_err(fuse_req_t req_,
 void
 fuse_reply_none(fuse_req_t req)
 {
-  if (req->ch)
-    fuse_chan_send(req->ch, NULL, 0);
   destroy_req(req);
 }
 
@@ -279,13 +260,13 @@ fill_open(struct fuse_open_out   *arg,
           const fuse_file_info_t *f)
 {
   arg->fh = f->fh;
-  if (f->direct_io)
+  if(f->direct_io)
     arg->open_flags |= FOPEN_DIRECT_IO;
-  if (f->keep_cache)
+  if(f->keep_cache)
     arg->open_flags |= FOPEN_KEEP_CACHE;
-  if (f->nonseekable)
+  if(f->nonseekable)
     arg->open_flags |= FOPEN_NONSEEKABLE;
-  if (f->cache_readdir)
+  if(f->cache_readdir)
     arg->open_flags |= FOPEN_CACHE_DIR;
 }
 
@@ -299,7 +280,7 @@ fuse_reply_entry(fuse_req_t                     req,
 
   /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant
      negative entry */
-  if (!e->ino && req->f->conn.proto_minor < 4)
+  if(!e->ino && req->f->conn.proto_minor < 4)
     return fuse_reply_err(req, ENOENT);
 
   fill_entry(&arg, e);
@@ -307,21 +288,27 @@ fuse_reply_entry(fuse_req_t                     req,
   return send_reply_ok(req, &arg, size);
 }
 
+struct fuse_create_out
+{
+  struct fuse_entry_out e;
+  struct fuse_open_out o;
+};
+
 int
 fuse_reply_create(fuse_req_t                     req,
                   const struct fuse_entry_param *e,
                   const fuse_file_info_t        *f)
 {
-  char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)] = {0};
+  struct fuse_create_out buf = {0};
   size_t entrysize = req->f->conn.proto_minor < 9 ?
     FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
-  struct fuse_entry_out *earg = (struct fuse_entry_out *) buf;
-  struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize);
+  struct fuse_entry_out *earg = (struct fuse_entry_out*)&buf.e;
+  struct fuse_open_out  *oarg = (struct fuse_open_out*)(((char*)&buf)+entrysize);
 
   fill_entry(earg, e);
   fill_open(oarg, f);
 
-  return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out));
+  return send_reply_ok(req, &buf, entrysize + sizeof(struct fuse_open_out));
 }
 
 int
@@ -391,7 +378,7 @@ fuse_send_data_iov_fallback(struct fuse_ll     *f,
   struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
 
   /* Optimize common case */
-  if (buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
+  if(buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
       !(buf->buf[0].flags & FUSE_BUF_IS_FD))
     {
       /* FIXME: also avoid memory copy if there are multiple buffers
@@ -405,12 +392,12 @@ fuse_send_data_iov_fallback(struct fuse_ll     *f,
     }
 
   res = posix_memalign(&mbuf, pagesize, len);
-  if (res != 0)
+  if(res != 0)
     return res;
 
   mem_buf.buf[0].mem = mbuf;
   res = fuse_buf_copy(&mem_buf, buf, 0);
-  if (res < 0)
+  if(res < 0)
     {
       free(mbuf);
       return -res;
@@ -448,22 +435,22 @@ struct fuse_ll_pipe*
 fuse_ll_get_pipe(struct fuse_ll *f)
 {
   struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
-  if (llp == NULL)
+  if(llp == NULL)
     {
       int res;
 
       llp = malloc(sizeof(struct fuse_ll_pipe));
-      if (llp == NULL)
+      if(llp == NULL)
         return NULL;
 
       res = pipe(llp->pipe);
-      if (res == -1)
+      if(res == -1)
         {
           free(llp);
           return NULL;
         }
 
-      if (fcntl(llp->pipe[0], F_SETFL, O_NONBLOCK) == -1 ||
+      if(fcntl(llp->pipe[0], F_SETFL, O_NONBLOCK) == -1 ||
           fcntl(llp->pipe[1], F_SETFL, O_NONBLOCK) == -1)
         {
           close(llp->pipe[0]);
@@ -491,7 +478,7 @@ fuse_ll_clear_pipe(struct fuse_ll *f)
 {
   struct fuse_ll_pipe *llp = pthread_getspecific(f->pipe_key);
 
-  if (llp)
+  if(llp)
     {
       pthread_setspecific(f->pipe_key, NULL);
       fuse_ll_pipe_free(llp);
@@ -508,13 +495,13 @@ read_back(int     fd,
   int res;
 
   res = read(fd, buf, len);
-  if (res == -1)
+  if(res == -1)
     {
       fprintf(stderr, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno));
       return -EIO;
     }
 
-  if (res != len)
+  if(res != len)
     {
       fprintf(stderr, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len);
       return -EIO;
@@ -543,31 +530,31 @@ fuse_send_data_iov(struct fuse_ll     *f,
   size_t headerlen;
   struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len);
 
-  if (f->broken_splice_nonblock)
+  if(f->broken_splice_nonblock)
     goto fallback;
 
-  if (flags & FUSE_BUF_NO_SPLICE)
+  if(flags & FUSE_BUF_NO_SPLICE)
     goto fallback;
 
   total_fd_size = 0;
   for (idx = buf->idx; idx < buf->count; idx++)
     {
-      if (buf->buf[idx].flags & FUSE_BUF_IS_FD)
+      if(buf->buf[idx].flags & FUSE_BUF_IS_FD)
         {
           total_fd_size = buf->buf[idx].size;
-          if (idx == buf->idx)
+          if(idx == buf->idx)
             total_fd_size -= buf->off;
         }
     }
 
-  if (total_fd_size < 2 * pagesize)
+  if(total_fd_size < 2 * pagesize)
     goto fallback;
 
-  if (f->conn.proto_minor < 14 || !(f->conn.want & FUSE_CAP_SPLICE_WRITE))
+  if(f->conn.proto_minor < 14 || !(f->conn.want & FUSE_CAP_SPLICE_WRITE))
     goto fallback;
 
   llp = fuse_ll_get_pipe(f);
-  if (llp == NULL)
+  if(llp == NULL)
     goto fallback;
 
   headerlen = iov_length(iov, iov_count);
@@ -580,12 +567,12 @@ fuse_send_data_iov(struct fuse_ll     *f,
    */
   pipesize = pagesize * (iov_count + buf->count + 1) + out->len;
 
-  if (llp->size < pipesize)
+  if(llp->size < pipesize)
     {
-      if (llp->can_grow)
+      if(llp->can_grow)
         {
           res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize);
-          if (res == -1)
+          if(res == -1)
             {
               llp->can_grow = 0;
               goto fallback;
@@ -593,15 +580,15 @@ fuse_send_data_iov(struct fuse_ll     *f,
           llp->size = res;
         }
 
-      if (llp->size < pipesize)
+      if(llp->size < pipesize)
         goto fallback;
     }
 
   res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK);
-  if (res == -1)
+  if(res == -1)
     goto fallback;
 
-  if (res != headerlen)
+  if(res != headerlen)
     {
       res = -EIO;
       fprintf(stderr, "fuse: short vmsplice to pipe: %u/%zu\n", res,
@@ -614,9 +601,9 @@ fuse_send_data_iov(struct fuse_ll     *f,
 
   res = fuse_buf_copy(&pipe_buf, buf,
                       FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK);
-  if (res < 0)
+  if(res < 0)
     {
-      if (res == -EAGAIN || res == -EINVAL)
+      if(res == -EAGAIN || res == -EINVAL)
         {
           /*
            * Should only get EAGAIN on kernels with
@@ -628,7 +615,7 @@ fuse_send_data_iov(struct fuse_ll     *f,
            * EINVAL might mean that splice can't handle
            * this combination of input and output.
            */
-          if (res == -EAGAIN)
+          if(res == -EAGAIN)
             f->broken_splice_nonblock = 1;
 
           pthread_setspecific(f->pipe_key, NULL);
@@ -639,7 +626,7 @@ fuse_send_data_iov(struct fuse_ll     *f,
       goto clear_pipe;
     }
 
-  if (res != 0 && res < len)
+  if(res != 0 && res < len)
     {
       struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
       void *mbuf;
@@ -654,13 +641,13 @@ fuse_send_data_iov(struct fuse_ll     *f,
        */
 
       res = posix_memalign(&mbuf, pagesize, len);
-      if (res != 0)
+      if(res != 0)
         goto clear_pipe;
 
       mem_buf.buf[0].mem = mbuf;
       mem_buf.off = now_len;
       res = fuse_buf_copy(&mem_buf, buf, 0);
-      if (res > 0)
+      if(res > 0)
         {
           char *tmpbuf;
           size_t extra_len = res;
@@ -670,7 +657,7 @@ fuse_send_data_iov(struct fuse_ll     *f,
            * back to regular write.
            */
           tmpbuf = malloc(headerlen);
-          if (tmpbuf == NULL)
+          if(tmpbuf == NULL)
             {
               free(mbuf);
               res = ENOMEM;
@@ -678,13 +665,13 @@ fuse_send_data_iov(struct fuse_ll     *f,
             }
           res = read_back(llp->pipe[0], tmpbuf, headerlen);
           free(tmpbuf);
-          if (res != 0)
+          if(res != 0)
             {
               free(mbuf);
               goto clear_pipe;
             }
           res = read_back(llp->pipe[0], mbuf, now_len);
-          if (res != 0)
+          if(res != 0)
             {
               free(mbuf);
               goto clear_pipe;
@@ -704,19 +691,19 @@ fuse_send_data_iov(struct fuse_ll     *f,
   out->len = headerlen + len;
 
   splice_flags = 0;
-  if ((flags & FUSE_BUF_SPLICE_MOVE) &&
+  if((flags & FUSE_BUF_SPLICE_MOVE) &&
       (f->conn.want & FUSE_CAP_SPLICE_MOVE))
     splice_flags |= SPLICE_F_MOVE;
 
   res = splice(llp->pipe[0], NULL, fuse_chan_fd(ch), NULL, out->len, splice_flags);
-  if (res == -1)
+  if(res == -1)
     {
       res = -errno;
       perror("fuse: splice from pipe");
       goto clear_pipe;
     }
 
-  if (res != out->len)
+  if(res != out->len)
     {
       res = -EIO;
       fprintf(stderr, "fuse: short splice from pipe: %u/%u\n",
@@ -766,7 +753,7 @@ fuse_reply_data(fuse_req_t                req,
   out.error = 0;
 
   res = fuse_send_data_iov(req->f, req->ch, iov, 1, bufv, flags);
-  if (res <= 0)
+  if(res <= 0)
     {
       destroy_req(req);
       return res;
@@ -808,10 +795,10 @@ fuse_reply_lock(fuse_req_t          req,
   struct fuse_lk_out arg = {0};
 
   arg.lk.type = lock->l_type;
-  if (lock->l_type != F_UNLCK)
+  if(lock->l_type != F_UNLCK)
     {
       arg.lk.start = lock->l_start;
-      if (lock->l_len == 0)
+      if(lock->l_len == 0)
         arg.lk.end = OFFSET_MAX;
       else
         arg.lk.end = lock->l_start + lock->l_len - 1;
@@ -841,7 +828,7 @@ fuse_ioctl_iovec_copy(const struct iovec *iov,
   size_t i;
 
   fiov = malloc(sizeof(fiov[0]) * count);
-  if (!fiov)
+  if(!fiov)
     return NULL;
 
   for (i = 0; i < count; i++)
@@ -874,16 +861,16 @@ fuse_reply_ioctl_retry(fuse_req_t          req,
   iov[count].iov_len = sizeof(arg);
   count++;
 
-  if (req->f->conn.proto_minor < 16)
+  if(req->f->conn.proto_minor < 16)
     {
-      if (in_count)
+      if(in_count)
         {
           iov[count].iov_base = (void *)in_iov;
           iov[count].iov_len = sizeof(in_iov[0]) * in_count;
           count++;
         }
 
-      if (out_count)
+      if(out_count)
         {
           iov[count].iov_base = (void *)out_iov;
           iov[count].iov_len = sizeof(out_iov[0]) * out_count;
@@ -899,20 +886,20 @@ fuse_reply_ioctl_retry(fuse_req_t          req,
           goto out;
         }
 
-      if (in_count)
+      if(in_count)
         {
           in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
-          if (!in_fiov)
+          if(!in_fiov)
             goto enomem;
 
           iov[count].iov_base = (void *)in_fiov;
           iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
           count++;
         }
-      if (out_count)
+      if(out_count)
         {
           out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
-          if (!out_fiov)
+          if(!out_fiov)
             goto enomem;
 
           iov[count].iov_base = (void *)out_fiov;
@@ -974,7 +961,7 @@ fuse_reply_ioctl_iov(fuse_req_t          req,
   int res;
 
   padded_iov = malloc((count + 2) * sizeof(struct iovec));
-  if (padded_iov == NULL)
+  if(padded_iov == NULL)
     return fuse_reply_err(req, ENOMEM);
 
   arg.result = result;
@@ -1002,531 +989,250 @@ fuse_reply_poll(fuse_req_t req,
 
 static
 void
-do_lookup(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_lookup(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  char *name = (char*)inarg;
-
-  req->f->op.lookup(req,nodeid,name);
+  req->f->op.lookup(req,hdr_);
 }
 
 static
 void
-do_forget(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_forget(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  struct fuse_forget_in *arg = (struct fuse_forget_in*)inarg;
-
-  req->f->op.forget(req,nodeid,arg->nlookup);
+  req->f->op.forget(req,hdr_);
 }
 
 static
 void
-do_batch_forget(fuse_req_t  req,
-                uint64_t    nodeid,
-                const void *inarg)
+do_batch_forget(fuse_req_t             req,
+                struct fuse_in_header *hdr_)
 {
-  struct fuse_batch_forget_in *arg = (void *) inarg;
-  struct fuse_forget_one *param = (void *) PARAM(arg);
-
-  (void)nodeid;
-
-  req->f->op.forget_multi(req,
-                          arg->count,
-                          (struct fuse_forget_data*)param);
+  req->f->op.forget_multi(req,hdr_);
 }
 
 static
 void
-do_getattr(fuse_req_t  req,
-           uint64_t    nodeid,
-           const void *inarg)
+do_getattr(fuse_req_t             req,
+           struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t *fip = NULL;
-  fuse_file_info_t fi = {0};
-
-  if(req->f->conn.proto_minor >= 9)
-    {
-      struct fuse_getattr_in *arg = (struct fuse_getattr_in*)inarg;
-
-      if(arg->getattr_flags & FUSE_GETATTR_FH)
-        {
-          fi.fh = arg->fh;
-          fip = &fi;
-        }
-    }
-
-  req->f->op.getattr(req, nodeid, fip);
+  req->f->op.getattr(req, hdr_);
 }
 
 static
 void
-do_setattr(fuse_req_t  req_,
-           uint64_t    nodeid_,
-           const void *inarg_)
+do_setattr(fuse_req_t             req_,
+           struct fuse_in_header *hdr_)
 {
-  struct stat stbuf = {0};
-  fuse_file_info_t *fi;
-  fuse_file_info_t fi_store;
-  struct fuse_setattr_in *arg;
-
-  fi = NULL;
-  arg = (struct fuse_setattr_in*)inarg_;
-
-  convert_attr(arg,&stbuf);
-
-  if(arg->valid & FATTR_FH)
-    {
-      arg->valid &= ~FATTR_FH;
-      memset(&fi_store,0,sizeof(fi_store));
-      fi = &fi_store;
-      fi->fh = arg->fh;
-    }
-
-  arg->valid &=
-    (FATTR_MODE      |
-     FATTR_UID       |
-     FATTR_GID       |
-     FATTR_SIZE      |
-     FATTR_ATIME     |
-     FATTR_MTIME     |
-     FATTR_CTIME     |
-     FATTR_ATIME_NOW |
-     FATTR_MTIME_NOW);
-
-  req_->f->op.setattr(req_,nodeid_,&stbuf,arg->valid,fi);
+  req_->f->op.setattr(req_,hdr_);
 }
 
 static
 void
-do_access(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_access(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  struct fuse_access_in *arg = (struct fuse_access_in *)inarg;
-
-  req->f->op.access(req, nodeid, arg->mask);
+  req->f->op.access(req,hdr_);
 }
 
 static
 void
-do_readlink(fuse_req_t  req,
-            uint64_t    nodeid,
-            const void *inarg)
+do_readlink(fuse_req_t             req,
+            struct fuse_in_header *hdr_)
 {
-  (void)inarg;
-
-  req->f->op.readlink(req, nodeid);
+  req->f->op.readlink(req,hdr_);
 }
 
 static
 void
-do_mknod(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_mknod(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg;
-  char *name = PARAM(arg);
-
-  if (req->f->conn.proto_minor >= 12)
-    req->ctx.umask = arg->umask;
-  else
-    name = (char*)inarg + FUSE_COMPAT_MKNOD_IN_SIZE;
-
-  req->f->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
+  req->f->op.mknod(req,hdr_);
 }
 
 static
 void
-do_mkdir(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_mkdir(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg;
-
-  if(req->f->conn.proto_minor >= 12)
-    req->ctx.umask = arg->umask;
-
-  req->f->op.mkdir(req, nodeid, PARAM(arg), arg->mode);
+  req->f->op.mkdir(req,hdr_);
 }
 
 static
 void
-do_unlink(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_unlink(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  char *name = (char*)inarg;
-
-  req->f->op.unlink(req,nodeid,name);
+  req->f->op.unlink(req,hdr_);
 }
 
 static
 void
-do_rmdir(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_rmdir(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  char *name = (char*)inarg;
-
-  req->f->op.rmdir(req, nodeid, name);
+  req->f->op.rmdir(req,hdr_);
 }
 
 static
 void
-do_symlink(fuse_req_t  req,
-           uint64_t    nodeid,
-           const void *inarg)
+do_symlink(fuse_req_t             req,
+           struct fuse_in_header *hdr_)
 {
-  char *name     = (char*)inarg;
-  char *linkname = (name + strlen(name) + 1);
-
-  req->f->op.symlink(req, linkname, nodeid, name);
+  req->f->op.symlink(req,hdr_);
 }
 
 static
 void
-do_rename(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_rename(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  struct fuse_rename_in *arg = (struct fuse_rename_in*)inarg;
-  char *oldname = PARAM(arg);
-  char *newname = oldname + strlen(oldname) + 1;
-
-  req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
+  req->f->op.rename(req,hdr_);
 }
 
 static
 void
-do_link(fuse_req_t  req,
-        uint64_t    nodeid,
-        const void *inarg)
+do_link(fuse_req_t             req,
+        struct fuse_in_header *hdr_)
 {
-  struct fuse_link_in *arg = (struct fuse_link_in*)inarg;
-
-  req->f->op.link(req,arg->oldnodeid,nodeid,PARAM(arg));
+  req->f->op.link(req,hdr_);
 }
 
 static
 void
-do_create(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_create(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  struct fuse_create_in *arg = (struct fuse_create_in*)inarg;
-  fuse_file_info_t fi = {0};
-  char *name = PARAM(arg);
-
-  fi.flags = arg->flags;
-
-  if (req->f->conn.proto_minor >= 12)
-    req->ctx.umask = arg->umask;
-  else
-    name = (char*)inarg + sizeof(struct fuse_open_in);
-
-  req->f->op.create(req, nodeid, name, arg->mode, &fi);
+  req->f->op.create(req,hdr_);
 }
 
 static
 void
-do_open(fuse_req_t  req,
-        uint64_t    nodeid,
-        const void *inarg)
+do_open(fuse_req_t             req,
+        struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_open_in *arg = (struct fuse_open_in*)inarg;
-
-  fi.flags = arg->flags;
-
-  req->f->op.open(req, nodeid, &fi);
+  req->f->op.open(req,hdr_);
 }
 
 static
 void
-do_read(fuse_req_t  req,
-        uint64_t    nodeid,
-        const void *inarg)
+do_read(fuse_req_t             req,
+        struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_read_in *arg = (struct fuse_read_in*)inarg;
-
-  fi.fh = arg->fh;
-  if (req->f->conn.proto_minor >= 9)
-    {
-      fi.lock_owner = arg->lock_owner;
-      fi.flags = arg->flags;
-    }
-
-  req->f->op.read(req, nodeid, arg->size, arg->offset, &fi);
+  req->f->op.read(req,hdr_);
 }
 
 static
 void
-do_write(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_write(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  char *param;
-  fuse_file_info_t fi = {0};
-  struct fuse_write_in *arg = (struct fuse_write_in*)inarg;
-
-  fi.fh = arg->fh;
-  fi.writepage = arg->write_flags & 1;
-
-  if(req->f->conn.proto_minor < 9)
-    {
-      param = ((char*)arg) + FUSE_COMPAT_WRITE_IN_SIZE;
-    }
-  else
-    {
-      fi.lock_owner = arg->lock_owner;
-      fi.flags = arg->flags;
-      param = PARAM(arg);
-    }
-
-  req->f->op.write(req,nodeid,param,arg->size,arg->offset,&fi);
+  req->f->op.write(req,hdr_);
 }
 
 static
 void
-do_write_buf(fuse_req_t             req,
-             uint64_t               nodeid,
-             const void            *inarg,
-             const struct fuse_buf *ibuf)
+do_flush(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  struct fuse_ll *f = req->f;
-  struct fuse_bufvec bufv = {
-                             .buf[0] = *ibuf,
-                             .count = 1,
-  };
-  fuse_file_info_t fi = {0};
-  struct fuse_write_in *arg = (struct fuse_write_in *) inarg;
-
-  fi.fh = arg->fh;
-  fi.writepage = arg->write_flags & 1;
-
-  if (req->f->conn.proto_minor < 9)
-    {
-      bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE;
-      bufv.buf[0].size -= sizeof(struct fuse_in_header) +
-        FUSE_COMPAT_WRITE_IN_SIZE;
-      assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD));
-    }
-  else
-    {
-      fi.lock_owner = arg->lock_owner;
-      fi.flags = arg->flags;
-      if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
-        bufv.buf[0].mem = PARAM(arg);
-
-      bufv.buf[0].size -= sizeof(struct fuse_in_header) +
-        sizeof(struct fuse_write_in);
-    }
-
-  if (bufv.buf[0].size < arg->size)
-    {
-      fprintf(stderr, "fuse: do_write_buf: buffer size too small\n");
-      fuse_reply_err(req, EIO);
-      goto out;
-    }
-  bufv.buf[0].size = arg->size;
-
-  req->f->op.write_buf(req, nodeid, &bufv, arg->offset, &fi);
-
- out:
-  /* Need to reset the pipe if ->write_buf() didn't consume all data */
-  if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
-    fuse_ll_clear_pipe(f);
+  req->f->op.flush(req,hdr_);
 }
 
 static
 void
-do_flush(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_release(fuse_req_t             req,
+           struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg;
-
-  fi.fh = arg->fh;
-  fi.flush = 1;
-  if(req->f->conn.proto_minor >= 7)
-    fi.lock_owner = arg->lock_owner;
-
-  req->f->op.flush(req,nodeid,&fi);
+  req->f->op.release(req,hdr_);
 }
 
 static
 void
-do_release(fuse_req_t  req,
-           uint64_t    nodeid,
-           const void *inarg)
+do_fsync(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_release_in *arg = (struct fuse_release_in*)inarg;
-
-  fi.flags = arg->flags;
-  fi.fh = arg->fh;
-  if(req->f->conn.proto_minor >= 8)
-    {
-      fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0;
-      fi.lock_owner = arg->lock_owner;
-    }
-
-  if(arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK)
-    {
-      fi.flock_release = 1;
-      fi.lock_owner = arg->lock_owner;
-    }
-
-  req->f->op.release(req,nodeid,&fi);
+  req->f->op.fsync(req,hdr_);
 }
 
 static
 void
-do_fsync(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_opendir(fuse_req_t             req,
+           struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_fsync_in *arg = (struct fuse_fsync_in*)inarg;
-
-  fi.fh = arg->fh;
-
-  req->f->op.fsync(req,nodeid,arg->fsync_flags & 1, &fi);
+  req->f->op.opendir(req,hdr_);
 }
 
 static
 void
-do_opendir(fuse_req_t  req,
-           uint64_t    nodeid,
-           const void *inarg)
+do_readdir(fuse_req_t             req,
+           struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_open_in *arg = (struct fuse_open_in*)inarg;
-
-  fi.flags = arg->flags;
-
-  req->f->op.opendir(req,nodeid,&fi);
+  req->f->op.readdir(req,hdr_);
 }
 
 static
 void
-do_readdir(fuse_req_t  req,
-           uint64_t    nodeid,
-           const void *inarg)
+do_readdir_plus(fuse_req_t             req_,
+                struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_read_in *arg = (struct fuse_read_in*)inarg;
-
-  fi.fh = arg->fh;
-
-  req->f->op.readdir(req,nodeid,arg->size,arg->offset,&fi);
+  req_->f->op.readdir_plus(req_,hdr_);
 }
 
 static
 void
-do_readdir_plus(fuse_req_t  req_,
-                uint64_t    nodeid_,
-                const void *inarg_)
+do_releasedir(fuse_req_t             req,
+              struct fuse_in_header *hdr_)
 {
-  const struct fuse_read_in *arg;
-  fuse_file_info_t ffi = {0};
-
-  arg    = (struct fuse_read_in*)inarg_;
-  ffi.fh = arg->fh;
-
-  req_->f->op.readdir_plus(req_,nodeid_,arg->size,arg->offset,&ffi);
+  req->f->op.releasedir(req,hdr_);
 }
 
 static
 void
-do_releasedir(fuse_req_t  req,
-              uint64_t    nodeid,
-              const void *inarg)
+do_fsyncdir(fuse_req_t             req,
+            struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_release_in *arg = (struct fuse_release_in*)inarg;
-
-  fi.flags = arg->flags;
-  fi.fh = arg->fh;
-
-  req->f->op.releasedir(req,nodeid,&fi);
+  req->f->op.fsyncdir(req,hdr_);
 }
 
 static
 void
-do_fsyncdir(fuse_req_t  req,
-            uint64_t    nodeid,
-            const void *inarg)
+do_statfs(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_fsync_in *arg = (struct fuse_fsync_in*)inarg;
-
-  fi.fh = arg->fh;
-
-  req->f->op.fsyncdir(req,nodeid,arg->fsync_flags & 1,&fi);
+  req->f->op.statfs(req,hdr_);
 }
 
 static
 void
-do_statfs(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_setxattr(fuse_req_t             req,
+            struct fuse_in_header *hdr_)
 {
-  (void)nodeid;
-  (void)inarg;
-
-  req->f->op.statfs(req, nodeid);
+  req->f->op.setxattr(req,hdr_);
 }
 
 static
 void
-do_setxattr(fuse_req_t  req,
-            uint64_t    nodeid,
-            const void *inarg)
+do_getxattr(fuse_req_t             req,
+            struct fuse_in_header *hdr_)
 {
-  struct fuse_setxattr_in *arg = (struct fuse_setxattr_in*)inarg;
-  char *name  = PARAM(arg);
-  char *value = name + strlen(name) + 1;
-
-  req->f->op.setxattr(req, nodeid, name, value, arg->size, arg->flags);
+  req->f->op.getxattr(req,hdr_);
 }
 
 static
 void
-do_getxattr(fuse_req_t  req,
-            uint64_t    nodeid,
-            const void *inarg)
+do_listxattr(fuse_req_t             req,
+             struct fuse_in_header *hdr_)
 {
-  struct fuse_getxattr_in *arg = (struct fuse_getxattr_in*)inarg;
-
-  req->f->op.getxattr(req, nodeid, PARAM(arg), arg->size);
+  req->f->op.listxattr(req,hdr_);
 }
 
 static
 void
-do_listxattr(fuse_req_t  req,
-             uint64_t    nodeid,
-             const void *inarg)
+do_removexattr(fuse_req_t             req,
+               struct fuse_in_header *hdr_)
 {
-  struct fuse_getxattr_in *arg = (struct fuse_getxattr_in*)inarg;
-
-  req->f->op.listxattr(req, nodeid, arg->size);
-}
-
-static
-void
-do_removexattr(fuse_req_t  req,
-               uint64_t    nodeid,
-               const void *inarg)
-{
-  char *name = (char *) inarg;
-
-  req->f->op.removexattr(req, nodeid, name);
+  req->f->op.removexattr(req,hdr_);
 }
 
 static
@@ -1538,7 +1244,7 @@ convert_fuse_file_lock(struct fuse_file_lock *fl,
   flock->l_type = fl->type;
   flock->l_whence = SEEK_SET;
   flock->l_start = fl->start;
-  if (fl->end == OFFSET_MAX)
+  if(fl->end == OFFSET_MAX)
     flock->l_len = 0;
   else
     flock->l_len = fl->end - fl->start + 1;
@@ -1547,20 +1253,10 @@ convert_fuse_file_lock(struct fuse_file_lock *fl,
 
 static
 void
-do_getlk(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_getlk(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct flock flock;
-  struct fuse_lk_in *arg = (struct fuse_lk_in*)inarg;
-
-  fi.fh         = arg->fh;
-  fi.lock_owner = arg->owner;
-
-  convert_fuse_file_lock(&arg->lk, &flock);
-
-  req->f->op.getlk(req, nodeid, &fi, &flock);
+  req->f->op.getlk(req,hdr_);
 }
 
 static
@@ -1572,12 +1268,12 @@ do_setlk_common(fuse_req_t  req,
 {
   struct flock flock;
   fuse_file_info_t fi = {0};
-  struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg;
+  struct fuse_lk_in *arg = (struct fuse_lk_in*)inarg;
 
   fi.fh = arg->fh;
   fi.lock_owner = arg->owner;
 
-  if (arg->lk_flags & FUSE_LK_FLOCK)
+  if(arg->lk_flags & FUSE_LK_FLOCK)
     {
       int op = 0;
 
@@ -1594,7 +1290,7 @@ do_setlk_common(fuse_req_t  req,
           break;
         }
 
-      if (!sleep)
+      if(!sleep)
         op |= LOCK_NB;
 
       req->f->op.flock(req,nodeid,&fi,op);
@@ -1609,71 +1305,42 @@ do_setlk_common(fuse_req_t  req,
 
 static
 void
-do_setlk(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+do_setlk(fuse_req_t             req,
+         struct fuse_in_header *hdr_)
 {
-  do_setlk_common(req, nodeid, inarg, 0);
+  do_setlk_common(req, hdr_->nodeid, &hdr_[1], 0);
 }
 
 static
 void
-do_setlkw(fuse_req_t  req,
-          uint64_t    nodeid,
-          const void *inarg)
+do_setlkw(fuse_req_t             req,
+          struct fuse_in_header *hdr_)
 {
-  do_setlk_common(req, nodeid, inarg, 1);
+  do_setlk_common(req, hdr_->nodeid, &hdr_[1], 1);
 }
 
 static
 void
 do_interrupt(fuse_req_t  req,
-             uint64_t    nodeid,
-             const void *inarg)
+             struct fuse_in_header *hdr_)
 {
   destroy_req(req);
 }
 
 static
 void
-do_bmap(fuse_req_t  req,
-        uint64_t    nodeid,
-        const void *inarg)
+do_bmap(fuse_req_t             req,
+        struct fuse_in_header *hdr_)
 {
-  struct fuse_bmap_in *arg = (struct fuse_bmap_in*)inarg;
-
-  req->f->op.bmap(req,nodeid,arg->blocksize,arg->block);
+  req->f->op.bmap(req,hdr_);
 }
 
 static
 void
 do_ioctl(fuse_req_t  req,
-         uint64_t    nodeid,
-         const void *inarg)
+         struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
-  unsigned int flags = arg->flags;
-  void *in_buf = arg->in_size ? PARAM(arg) : NULL;
-
-  if((flags & FUSE_IOCTL_DIR) && !(req->f->conn.want & FUSE_CAP_IOCTL_DIR))
-    {
-      fuse_reply_err(req,ENOTTY);
-      return;
-    }
-
-  fi.fh = arg->fh;
-
-  if((sizeof(void *) == 4) &&
-     (req->f->conn.proto_minor >= 16) &&
-     !(flags & FUSE_IOCTL_32BIT))
-    {
-      req->ioctl_64bit = 1;
-    }
-
-  req->f->op.ioctl(req, nodeid, (unsigned long)arg->cmd,
-                   (void *)(uintptr_t)arg->arg, &fi, flags,
-                   in_buf, arg->in_size, arg->out_size);
+  req->f->op.ioctl(req, hdr_);
 }
 
 void
@@ -1684,58 +1351,30 @@ fuse_pollhandle_destroy(fuse_pollhandle_t *ph)
 
 static
 void
-do_poll(fuse_req_t  req,
-        uint64_t    nodeid,
-        const void *inarg)
+do_poll(fuse_req_t             req,
+        struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  fuse_pollhandle_t *ph = NULL;
-  struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg;
-
-  fi.fh = arg->fh;
-
-  if(arg->flags & FUSE_POLL_SCHEDULE_NOTIFY)
-    {
-      ph = malloc(sizeof(fuse_pollhandle_t));
-      if (ph == NULL) {
-        fuse_reply_err(req, ENOMEM);
-        return;
-      }
-      ph->kh = arg->kh;
-      ph->ch = req->ch;
-      ph->f = req->f;
-    }
-
-  req->f->op.poll(req,nodeid,&fi,ph);
+  req->f->op.poll(req,hdr_);
 }
 
 static
 void
-do_fallocate(fuse_req_t  req,
-             uint64_t    nodeid,
-             const void *inarg)
+do_fallocate(fuse_req_t             req,
+             struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t fi = {0};
-  struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg;
-
-  fi.fh = arg->fh;
-
-  req->f->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi);
+  req->f->op.fallocate(req,hdr_);
 }
 
 static
 void
-do_init(fuse_req_t  req,
-        uint64_t    nodeid,
-        const void *inarg)
+do_init(fuse_req_t             req,
+        struct fuse_in_header *hdr_)
 {
   struct fuse_init_out outarg = {0};
-  struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
+  struct fuse_init_in *arg = (struct fuse_init_in *) &hdr_[1];
   struct fuse_ll *f = req->f;
   size_t bufsize = fuse_chan_bufsize(req->ch);
 
-  (void)nodeid;
-
   if(f->debug)
     debug_fuse_init_in(arg);
 
@@ -1748,7 +1387,7 @@ do_init(fuse_req_t  req,
   outarg.minor     = FUSE_KERNEL_MINOR_VERSION;
   outarg.max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
 
-  if (arg->major < 7)
+  if(arg->major < 7)
     {
       fprintf(stderr, "fuse: unsupported protocol version: %u.%u\n",
               arg->major, arg->minor);
@@ -1756,47 +1395,49 @@ do_init(fuse_req_t  req,
       return;
     }
 
-  if (arg->major > 7)
+  if(arg->major > 7)
     {
       /* Wait for a second INIT request with a 7.X version */
       send_reply_ok(req, &outarg, sizeof(outarg));
       return;
     }
 
-  if (arg->minor >= 6)
+  if(arg->minor >= 6)
     {
-      if (arg->max_readahead < f->conn.max_readahead)
+      if(arg->max_readahead < f->conn.max_readahead)
         f->conn.max_readahead = arg->max_readahead;
-      if (arg->flags & FUSE_ASYNC_READ)
+      if(arg->flags & FUSE_ASYNC_READ)
         f->conn.capable |= FUSE_CAP_ASYNC_READ;
-      if (arg->flags & FUSE_POSIX_LOCKS)
+      if(arg->flags & FUSE_POSIX_LOCKS)
         f->conn.capable |= FUSE_CAP_POSIX_LOCKS;
-      if (arg->flags & FUSE_ATOMIC_O_TRUNC)
+      if(arg->flags & FUSE_ATOMIC_O_TRUNC)
         f->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
-      if (arg->flags & FUSE_EXPORT_SUPPORT)
+      if(arg->flags & FUSE_EXPORT_SUPPORT)
         f->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
-      if (arg->flags & FUSE_BIG_WRITES)
+      if(arg->flags & FUSE_BIG_WRITES)
         f->conn.capable |= FUSE_CAP_BIG_WRITES;
-      if (arg->flags & FUSE_DONT_MASK)
+      if(arg->flags & FUSE_DONT_MASK)
         f->conn.capable |= FUSE_CAP_DONT_MASK;
-      if (arg->flags & FUSE_FLOCK_LOCKS)
+      if(arg->flags & FUSE_FLOCK_LOCKS)
         f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
-      if (arg->flags & FUSE_POSIX_ACL)
+      if(arg->flags & FUSE_POSIX_ACL)
         f->conn.capable |= FUSE_CAP_POSIX_ACL;
-      if (arg->flags & FUSE_CACHE_SYMLINKS)
+      if(arg->flags & FUSE_CACHE_SYMLINKS)
         f->conn.capable |= FUSE_CAP_CACHE_SYMLINKS;
-      if (arg->flags & FUSE_ASYNC_DIO)
+      if(arg->flags & FUSE_ASYNC_DIO)
         f->conn.capable |= FUSE_CAP_ASYNC_DIO;
-      if (arg->flags & FUSE_PARALLEL_DIROPS)
+      if(arg->flags & FUSE_PARALLEL_DIROPS)
         f->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
-      if (arg->flags & FUSE_MAX_PAGES)
+      if(arg->flags & FUSE_MAX_PAGES)
         f->conn.capable |= FUSE_CAP_MAX_PAGES;
-      if (arg->flags & FUSE_WRITEBACK_CACHE)
+      if(arg->flags & FUSE_WRITEBACK_CACHE)
         f->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
-      if (arg->flags & FUSE_DO_READDIRPLUS)
+      if(arg->flags & FUSE_DO_READDIRPLUS)
         f->conn.capable |= FUSE_CAP_READDIR_PLUS;
-      if (arg->flags & FUSE_READDIRPLUS_AUTO)
+      if(arg->flags & FUSE_READDIRPLUS_AUTO)
         f->conn.capable |= FUSE_CAP_READDIR_PLUS_AUTO;
+      if(arg->flags & FUSE_SETXATTR_EXT)
+        f->conn.capable |= FUSE_CAP_SETXATTR_EXT;
     }
   else
     {
@@ -1804,31 +1445,31 @@ do_init(fuse_req_t  req,
       f->conn.max_readahead = 0;
     }
 
-  if (req->f->conn.proto_minor >= 14)
+  if(req->f->conn.proto_minor >= 14)
     {
 #ifdef HAVE_SPLICE
 #ifdef HAVE_VMSPLICE
       f->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
-      if (f->splice_write)
+      if(f->splice_write)
         f->conn.want |= FUSE_CAP_SPLICE_WRITE;
-      if (f->splice_move)
+      if(f->splice_move)
         f->conn.want |= FUSE_CAP_SPLICE_MOVE;
 #endif
       f->conn.capable |= FUSE_CAP_SPLICE_READ;
-      if (f->splice_read)
+      if(f->splice_read)
         f->conn.want |= FUSE_CAP_SPLICE_READ;
 #endif
     }
 
-  if (req->f->conn.proto_minor >= 18)
+  if(req->f->conn.proto_minor >= 18)
     f->conn.capable |= FUSE_CAP_IOCTL_DIR;
 
-  if (f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
+  if(f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
     f->conn.want |= FUSE_CAP_POSIX_LOCKS;
-  if (f->op.flock && !f->no_remote_flock)
+  if(f->op.flock && !f->no_remote_flock)
     f->conn.want |= FUSE_CAP_FLOCK_LOCKS;
 
-  if (bufsize < FUSE_MIN_READ_BUFFER)
+  if(bufsize < FUSE_MIN_READ_BUFFER)
     {
       fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
               bufsize);
@@ -1836,64 +1477,66 @@ do_init(fuse_req_t  req,
     }
 
   bufsize -= 4096;
-  if (bufsize < f->conn.max_write)
+  if(bufsize < f->conn.max_write)
     f->conn.max_write = bufsize;
 
   f->got_init = 1;
-  if (f->op.init)
+  if(f->op.init)
     f->op.init(f->userdata, &f->conn);
 
-  if (f->no_splice_read)
+  if(f->no_splice_read)
     f->conn.want &= ~FUSE_CAP_SPLICE_READ;
-  if (f->no_splice_write)
+  if(f->no_splice_write)
     f->conn.want &= ~FUSE_CAP_SPLICE_WRITE;
-  if (f->no_splice_move)
+  if(f->no_splice_move)
     f->conn.want &= ~FUSE_CAP_SPLICE_MOVE;
 
-  if ((arg->flags & FUSE_MAX_PAGES) && (f->conn.want & FUSE_CAP_MAX_PAGES))
+  if((arg->flags & FUSE_MAX_PAGES) && (f->conn.want & FUSE_CAP_MAX_PAGES))
     {
       outarg.flags     |= FUSE_MAX_PAGES;
       outarg.max_pages  = f->conn.max_pages;
     }
 
-  if (f->conn.want & FUSE_CAP_ASYNC_READ)
+  if(f->conn.want & FUSE_CAP_ASYNC_READ)
     outarg.flags |= FUSE_ASYNC_READ;
-  if (f->conn.want & FUSE_CAP_POSIX_LOCKS)
+  if(f->conn.want & FUSE_CAP_POSIX_LOCKS)
     outarg.flags |= FUSE_POSIX_LOCKS;
-  if (f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
+  if(f->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
     outarg.flags |= FUSE_ATOMIC_O_TRUNC;
-  if (f->conn.want & FUSE_CAP_EXPORT_SUPPORT)
+  if(f->conn.want & FUSE_CAP_EXPORT_SUPPORT)
     outarg.flags |= FUSE_EXPORT_SUPPORT;
-  if (f->conn.want & FUSE_CAP_BIG_WRITES)
+  if(f->conn.want & FUSE_CAP_BIG_WRITES)
     outarg.flags |= FUSE_BIG_WRITES;
-  if (f->conn.want & FUSE_CAP_DONT_MASK)
+  if(f->conn.want & FUSE_CAP_DONT_MASK)
     outarg.flags |= FUSE_DONT_MASK;
-  if (f->conn.want & FUSE_CAP_FLOCK_LOCKS)
+  if(f->conn.want & FUSE_CAP_FLOCK_LOCKS)
     outarg.flags |= FUSE_FLOCK_LOCKS;
-  if (f->conn.want & FUSE_CAP_POSIX_ACL)
+  if(f->conn.want & FUSE_CAP_POSIX_ACL)
     outarg.flags |= FUSE_POSIX_ACL;
-  if (f->conn.want & FUSE_CAP_CACHE_SYMLINKS)
+  if(f->conn.want & FUSE_CAP_CACHE_SYMLINKS)
     outarg.flags |= FUSE_CACHE_SYMLINKS;
-  if (f->conn.want & FUSE_CAP_ASYNC_DIO)
+  if(f->conn.want & FUSE_CAP_ASYNC_DIO)
     outarg.flags |= FUSE_ASYNC_DIO;
-  if (f->conn.want & FUSE_CAP_PARALLEL_DIROPS)
+  if(f->conn.want & FUSE_CAP_PARALLEL_DIROPS)
     outarg.flags |= FUSE_PARALLEL_DIROPS;
-  if (f->conn.want & FUSE_CAP_WRITEBACK_CACHE)
+  if(f->conn.want & FUSE_CAP_WRITEBACK_CACHE)
     outarg.flags |= FUSE_WRITEBACK_CACHE;
-  if (f->conn.want & FUSE_CAP_READDIR_PLUS)
+  if(f->conn.want & FUSE_CAP_READDIR_PLUS)
     outarg.flags |= FUSE_DO_READDIRPLUS;
-  if (f->conn.want & FUSE_CAP_READDIR_PLUS_AUTO)
+  if(f->conn.want & FUSE_CAP_READDIR_PLUS_AUTO)
     outarg.flags |= FUSE_READDIRPLUS_AUTO;
+  if(f->conn.want & FUSE_CAP_SETXATTR_EXT)
+    outarg.flags |= FUSE_SETXATTR_EXT;
 
   outarg.max_readahead = f->conn.max_readahead;
   outarg.max_write = f->conn.max_write;
-  if (f->conn.proto_minor >= 13)
+  if(f->conn.proto_minor >= 13)
     {
-      if (f->conn.max_background >= (1 << 16))
+      if(f->conn.max_background >= (1 << 16))
         f->conn.max_background = (1 << 16) - 1;
-      if (f->conn.congestion_threshold > f->conn.max_background)
+      if(f->conn.congestion_threshold > f->conn.max_background)
         f->conn.congestion_threshold = f->conn.max_background;
-      if (!f->conn.congestion_threshold)
+      if(!f->conn.congestion_threshold)
         {
           f->conn.congestion_threshold = f->conn.max_background * 3 / 4;
         }
@@ -1918,15 +1561,11 @@ do_init(fuse_req_t  req,
 
 static
 void
-do_destroy(fuse_req_t  req,
-           uint64_t    nodeid,
-           const void *inarg)
+do_destroy(fuse_req_t             req,
+           struct fuse_in_header *hdr_)
 {
   struct fuse_ll *f = req->f;
 
-  (void) nodeid;
-  (void) inarg;
-
   f->got_destroy = 1;
   f->op.destroy(f->userdata);
 
@@ -1966,9 +1605,7 @@ list_init_nreq(struct fuse_notify_req *nreq)
 static
 void
 do_notify_reply(fuse_req_t             req,
-                uint64_t             nodeid,
-                const void            *inarg,
-                const struct fuse_buf *buf)
+                struct fuse_in_header *hdr_)
 {
   struct fuse_ll *f = req->f;
   struct fuse_notify_req *nreq;
@@ -1976,9 +1613,9 @@ do_notify_reply(fuse_req_t             req,
 
   pthread_mutex_lock(&f->lock);
   head = &f->notify_list;
-  for (nreq = head->next; nreq != head; nreq = nreq->next)
+  for(nreq = head->next; nreq != head; nreq = nreq->next)
     {
-      if (nreq->unique == req->unique)
+      if(nreq->unique == req->unique)
         {
           list_del_nreq(nreq);
           break;
@@ -1986,33 +1623,16 @@ do_notify_reply(fuse_req_t             req,
     }
   pthread_mutex_unlock(&f->lock);
 
-  if (nreq != head)
-    nreq->reply(nreq, req, nodeid, inarg, buf);
+  if(nreq != head)
+    nreq->reply(nreq, req, hdr_->nodeid, &hdr_[1]);
 }
 
 static
 void
-do_copy_file_range(fuse_req_t  req_,
-                   uint64_t  nodeid_in_,
-                   const void *arg_)
-
+do_copy_file_range(fuse_req_t             req_,
+                   struct fuse_in_header *hdr_)
 {
-  fuse_file_info_t ffi_in  = {0};
-  fuse_file_info_t ffi_out = {0};
-  struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in*)arg_;
-
-  ffi_in.fh  = arg->fh_in;
-  ffi_out.fh = arg->fh_out;
-
-  req_->f->op.copy_file_range(req_,
-                              nodeid_in_,
-                              arg->off_in,
-                              &ffi_in,
-                              arg->nodeid_out,
-                              arg->off_out,
-                              &ffi_out,
-                              arg->len,
-                              arg->flags);
+  req_->f->op.copy_file_range(req_,hdr_);
 }
 
 static
@@ -2025,7 +1645,7 @@ send_notify_iov(struct fuse_ll   *f,
 {
   struct fuse_out_header out;
 
-  if (!f->got_init)
+  if(!f->got_init)
     return -ENOTCONN;
 
   out.unique = 0;
@@ -2039,7 +1659,7 @@ send_notify_iov(struct fuse_ll   *f,
 int
 fuse_lowlevel_notify_poll(fuse_pollhandle_t *ph)
 {
-  if (ph != NULL)
+  if(ph != NULL)
     {
       struct fuse_notify_poll_wakeup_out outarg;
       struct iovec iov[2];
@@ -2059,7 +1679,7 @@ fuse_lowlevel_notify_poll(fuse_pollhandle_t *ph)
 
 int
 fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch,
-                                 uint64_t        ino,
+                                 uint64_t          ino,
                                  off_t             off,
                                  off_t             len)
 {
@@ -2067,11 +1687,11 @@ fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch,
   struct fuse_ll *f;
   struct iovec iov[2];
 
-  if (!ch)
+  if(!ch)
     return -EINVAL;
 
-  f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
-  if (!f)
+  f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
+  if(!f)
     return -ENODEV;
 
   outarg.ino = ino;
@@ -2086,7 +1706,7 @@ fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch,
 
 int
 fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch,
-                                 uint64_t        parent,
+                                 uint64_t          parent,
                                  const char       *name,
                                  size_t            namelen)
 {
@@ -2094,11 +1714,11 @@ fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch,
   struct fuse_ll *f;
   struct iovec iov[3];
 
-  if (!ch)
+  if(!ch)
     return -EINVAL;
 
-  f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
-  if (!f)
+  f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
+  if(!f)
     return -ENODEV;
 
   outarg.parent = parent;
@@ -2115,8 +1735,8 @@ fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch,
 
 int
 fuse_lowlevel_notify_delete(struct fuse_chan *ch,
-                            uint64_t        parent,
-                            uint64_t        child,
+                            uint64_t          parent,
+                            uint64_t          child,
                             const char       *name,
                             size_t            namelen)
 {
@@ -2124,14 +1744,14 @@ fuse_lowlevel_notify_delete(struct fuse_chan *ch,
   struct fuse_ll *f;
   struct iovec iov[3];
 
-  if (!ch)
+  if(!ch)
     return -EINVAL;
 
-  f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
-  if (!f)
+  f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
+  if(!f)
     return -ENODEV;
 
-  if (f->conn.proto_minor < 18)
+  if(f->conn.proto_minor < 18)
     return -ENOSYS;
 
   outarg.parent = parent;
@@ -2149,7 +1769,7 @@ fuse_lowlevel_notify_delete(struct fuse_chan *ch,
 
 int
 fuse_lowlevel_notify_store(struct fuse_chan         *ch,
-                           uint64_t                ino,
+                           uint64_t                  ino,
                            off_t                     offset,
                            struct fuse_bufvec       *bufv,
                            enum fuse_buf_copy_flags  flags)
@@ -2161,14 +1781,14 @@ fuse_lowlevel_notify_store(struct fuse_chan         *ch,
   size_t size = fuse_buf_size(bufv);
   int res;
 
-  if (!ch)
+  if(!ch)
     return -EINVAL;
 
-  f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
-  if (!f)
+  f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
+  if(!f)
     return -ENODEV;
 
-  if (f->conn.proto_minor < 15)
+  if(f->conn.proto_minor < 15)
     return -ENOSYS;
 
   out.unique = 0;
@@ -2185,7 +1805,7 @@ fuse_lowlevel_notify_store(struct fuse_chan         *ch,
   iov[1].iov_len = sizeof(outarg);
 
   res = fuse_send_data_iov(f, ch, iov, 2, bufv, flags);
-  if (res > 0)
+  if(res > 0)
     res = -res;
 
   return res;
@@ -2201,52 +1821,20 @@ static
 void
 fuse_ll_retrieve_reply(struct fuse_notify_req *nreq,
                        fuse_req_t              req,
-                       uint64_t              ino,
-                       const void             *inarg,
-                       const struct fuse_buf  *ibuf)
+                       uint64_t                ino,
+                       const void             *inarg)
 {
-  struct fuse_ll *f = req->f;
   struct fuse_retrieve_req *rreq =
     container_of(nreq, struct fuse_retrieve_req, nreq);
-  const struct fuse_notify_retrieve_in *arg = inarg;
-  struct fuse_bufvec bufv = {
-                             .buf[0] = *ibuf,
-                             .count = 1,
-  };
 
-  if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD))
-    bufv.buf[0].mem = PARAM(arg);
+  fuse_reply_none(req);
 
-  bufv.buf[0].size -= sizeof(struct fuse_in_header) +
-    sizeof(struct fuse_notify_retrieve_in);
-
-  if (bufv.buf[0].size < arg->size)
-    {
-      fprintf(stderr, "fuse: retrieve reply: buffer size too small\n");
-      fuse_reply_none(req);
-      goto out;
-    }
-  bufv.buf[0].size = arg->size;
-
-  if (req->f->op.retrieve_reply)
-    {
-      req->f->op.retrieve_reply(req, rreq->cookie, ino,
-                                arg->offset, &bufv);
-    }
-  else
-    {
-      fuse_reply_none(req);
-    }
-
- out:
   free(rreq);
-  if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count)
-    fuse_ll_clear_pipe(f);
 }
 
 int
 fuse_lowlevel_notify_retrieve(struct fuse_chan *ch,
-                              uint64_t        ino,
+                              uint64_t          ino,
                               size_t            size,
                               off_t             offset,
                               void             *cookie)
@@ -2257,18 +1845,18 @@ fuse_lowlevel_notify_retrieve(struct fuse_chan *ch,
   struct fuse_retrieve_req *rreq;
   int err;
 
-  if (!ch)
+  if(!ch)
     return -EINVAL;
 
-  f = (struct fuse_ll *)fuse_session_data(fuse_chan_session(ch));
-  if (!f)
+  f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
+  if(!f)
     return -ENODEV;
 
-  if (f->conn.proto_minor < 15)
+  if(f->conn.proto_minor < 15)
     return -ENOSYS;
 
   rreq = malloc(sizeof(*rreq));
-  if (rreq == NULL)
+  if(rreq == NULL)
     return -ENOMEM;
 
   pthread_mutex_lock(&f->lock);
@@ -2287,7 +1875,7 @@ fuse_lowlevel_notify_retrieve(struct fuse_chan *ch,
   iov[1].iov_len = sizeof(outarg);
 
   err = send_notify_iov(f, ch, FUSE_NOTIFY_RETRIEVE, iov, 2);
-  if (err)
+  if(err)
     {
       pthread_mutex_lock(&f->lock);
       list_del_nreq(&rreq->nreq);
@@ -2312,247 +1900,86 @@ fuse_req_ctx(fuse_req_t req)
 }
 
 static struct {
-  void (*func)(fuse_req_t, uint64_t, const void *);
+  void (*func)(fuse_req_t, struct fuse_in_header *);
   const char *name;
 } fuse_ll_ops[] =
   {
-   [FUSE_LOOKUP]          = { do_lookup,          "LOOKUP"	     },
-   [FUSE_FORGET]          = { do_forget,          "FORGET"	     },
-   [FUSE_GETATTR]         = { do_getattr,         "GETATTR"         },
-   [FUSE_SETATTR]         = { do_setattr,         "SETATTR"         },
-   [FUSE_READLINK]        = { do_readlink,        "READLINK"        },
-   [FUSE_SYMLINK]         = { do_symlink,         "SYMLINK"         },
-   [FUSE_MKNOD]           = { do_mknod,           "MKNOD"	     },
-   [FUSE_MKDIR]           = { do_mkdir,           "MKDIR"	     },
-   [FUSE_UNLINK]          = { do_unlink,          "UNLINK"	     },
-   [FUSE_RMDIR]           = { do_rmdir,           "RMDIR"	     },
-   [FUSE_RENAME]          = { do_rename,          "RENAME"	     },
-   [FUSE_LINK]            = { do_link,	           "LINK"	     },
-   [FUSE_OPEN]            = { do_open,	           "OPEN"	     },
-   [FUSE_READ]            = { do_read,	           "READ"	     },
-   [FUSE_WRITE]           = { do_write,           "WRITE"	     },
-   [FUSE_STATFS]          = { do_statfs,          "STATFS"	     },
-   [FUSE_RELEASE]         = { do_release,         "RELEASE"         },
-   [FUSE_FSYNC]           = { do_fsync,           "FSYNC"	     },
-   [FUSE_SETXATTR]        = { do_setxattr,        "SETXATTR"        },
-   [FUSE_GETXATTR]        = { do_getxattr,        "GETXATTR"        },
-   [FUSE_LISTXATTR]       = { do_listxattr,       "LISTXATTR"       },
-   [FUSE_REMOVEXATTR]     = { do_removexattr,     "REMOVEXATTR"     },
-   [FUSE_FLUSH]           = { do_flush,           "FLUSH"	     },
-   [FUSE_INIT]            = { do_init,	           "INIT"	     },
-   [FUSE_OPENDIR]         = { do_opendir,         "OPENDIR"         },
-   [FUSE_READDIR]         = { do_readdir,         "READDIR"         },
-   [FUSE_READDIRPLUS]     = { do_readdir_plus,    "READDIR_PLUS"    },
-   [FUSE_RELEASEDIR]      = { do_releasedir,      "RELEASEDIR"      },
-   [FUSE_FSYNCDIR]        = { do_fsyncdir,        "FSYNCDIR"        },
-   [FUSE_GETLK]           = { do_getlk,           "GETLK"	     },
-   [FUSE_SETLK]           = { do_setlk,           "SETLK"	     },
-   [FUSE_SETLKW]          = { do_setlkw,          "SETLKW"	     },
-   [FUSE_ACCESS]          = { do_access,          "ACCESS"	     },
-   [FUSE_CREATE]          = { do_create,          "CREATE"	     },
-   [FUSE_INTERRUPT]       = { do_interrupt,       "INTERRUPT"       },
-   [FUSE_BMAP]            = { do_bmap,	           "BMAP"	     },
-   [FUSE_IOCTL]           = { do_ioctl,           "IOCTL"	     },
-   [FUSE_POLL]            = { do_poll,            "POLL"	     },
-   [FUSE_FALLOCATE]       = { do_fallocate,       "FALLOCATE"       },
-   [FUSE_DESTROY]         = { do_destroy,         "DESTROY"         },
-   [FUSE_NOTIFY_REPLY]    = { (void *) 1,         "NOTIFY_REPLY"    },
-   [FUSE_BATCH_FORGET]    = { do_batch_forget,    "BATCH_FORGET"    },
-   [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
+    [FUSE_LOOKUP]          = { do_lookup,          "LOOKUP"	     },
+    [FUSE_FORGET]          = { do_forget,          "FORGET"	     },
+    [FUSE_GETATTR]         = { do_getattr,         "GETATTR"         },
+    [FUSE_SETATTR]         = { do_setattr,         "SETATTR"         },
+    [FUSE_READLINK]        = { do_readlink,        "READLINK"        },
+    [FUSE_SYMLINK]         = { do_symlink,         "SYMLINK"         },
+    [FUSE_MKNOD]           = { do_mknod,           "MKNOD"	     },
+    [FUSE_MKDIR]           = { do_mkdir,           "MKDIR"	     },
+    [FUSE_UNLINK]          = { do_unlink,          "UNLINK"	     },
+    [FUSE_RMDIR]           = { do_rmdir,           "RMDIR"	     },
+    [FUSE_RENAME]          = { do_rename,          "RENAME"	     },
+    [FUSE_LINK]            = { do_link,	           "LINK"	     },
+    [FUSE_OPEN]            = { do_open,	           "OPEN"	     },
+    [FUSE_READ]            = { do_read,	           "READ"	     },
+    [FUSE_WRITE]           = { do_write,           "WRITE"	     },
+    [FUSE_STATFS]          = { do_statfs,          "STATFS"	     },
+    [FUSE_RELEASE]         = { do_release,         "RELEASE"         },
+    [FUSE_FSYNC]           = { do_fsync,           "FSYNC"	     },
+    [FUSE_SETXATTR]        = { do_setxattr,        "SETXATTR"        },
+    [FUSE_GETXATTR]        = { do_getxattr,        "GETXATTR"        },
+    [FUSE_LISTXATTR]       = { do_listxattr,       "LISTXATTR"       },
+    [FUSE_REMOVEXATTR]     = { do_removexattr,     "REMOVEXATTR"     },
+    [FUSE_FLUSH]           = { do_flush,           "FLUSH"	     },
+    [FUSE_INIT]            = { do_init,	           "INIT"	     },
+    [FUSE_OPENDIR]         = { do_opendir,         "OPENDIR"         },
+    [FUSE_READDIR]         = { do_readdir,         "READDIR"         },
+    [FUSE_READDIRPLUS]     = { do_readdir_plus,    "READDIR_PLUS"    },
+    [FUSE_RELEASEDIR]      = { do_releasedir,      "RELEASEDIR"      },
+    [FUSE_FSYNCDIR]        = { do_fsyncdir,        "FSYNCDIR"        },
+    [FUSE_GETLK]           = { do_getlk,           "GETLK"	     },
+    [FUSE_SETLK]           = { do_setlk,           "SETLK"	     },
+    [FUSE_SETLKW]          = { do_setlkw,          "SETLKW"	     },
+    [FUSE_ACCESS]          = { do_access,          "ACCESS"	     },
+    [FUSE_CREATE]          = { do_create,          "CREATE"	     },
+    [FUSE_INTERRUPT]       = { do_interrupt,       "INTERRUPT"       },
+    [FUSE_BMAP]            = { do_bmap,	           "BMAP"	     },
+    [FUSE_IOCTL]           = { do_ioctl,           "IOCTL"	     },
+    [FUSE_POLL]            = { do_poll,            "POLL"	     },
+    [FUSE_FALLOCATE]       = { do_fallocate,       "FALLOCATE"       },
+    [FUSE_DESTROY]         = { do_destroy,         "DESTROY"         },
+    [FUSE_NOTIFY_REPLY]    = { do_notify_reply,    "NOTIFY_REPLY"    },
+    [FUSE_BATCH_FORGET]    = { do_batch_forget,    "BATCH_FORGET"    },
+    [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" },
   };
 
 #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
 
-static
-int
-fuse_ll_copy_from_pipe(struct fuse_bufvec *dst,
-                       struct fuse_bufvec *src)
-{
-  int res = fuse_buf_copy(dst, src, 0);
-  if (res < 0)
-    {
-      fprintf(stderr, "fuse: copy from pipe: %s\n", strerror(-res));
-      return res;
-    }
-
-  if (res < fuse_buf_size(dst))
-    {
-      fprintf(stderr, "fuse: copy from pipe: short read\n");
-      return -1;
-    }
-
-  return 0;
-}
-
-static
-void
-fuse_ll_process_buf(void                  *data,
-                    const struct fuse_buf *buf,
-                    struct fuse_chan      *ch)
-{
-  struct fuse_ll *f = (struct fuse_ll *) data;
-  const size_t write_header_size = sizeof(struct fuse_in_header) +
-    sizeof(struct fuse_write_in);
-  struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 };
-  struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size);
-  struct fuse_in_header *in;
-  const void *inarg;
-  struct fuse_req *req;
-  void *mbuf = NULL;
-  int err;
-  int res;
-
-  if (buf->flags & FUSE_BUF_IS_FD)
-    {
-      if (buf->size < tmpbuf.buf[0].size)
-        tmpbuf.buf[0].size = buf->size;
-
-      mbuf = malloc(tmpbuf.buf[0].size);
-      if (mbuf == NULL)
-        {
-          fprintf(stderr, "fuse: failed to allocate header\n");
-          goto clear_pipe;
-        }
-      tmpbuf.buf[0].mem = mbuf;
-
-      res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
-      if (res < 0)
-        goto clear_pipe;
-
-      in = mbuf;
-    }
-  else
-    {
-      in = buf->mem;
-    }
-
-  req = fuse_ll_alloc_req(f);
-  if (req == NULL)
-    {
-      struct fuse_out_header out = {
-                                    .unique = in->unique,
-                                    .error = -ENOMEM,
-      };
-      struct iovec iov = {
-                          .iov_base = &out,
-                          .iov_len = sizeof(struct fuse_out_header),
-      };
-
-      fuse_send_msg(f, ch, &iov, 1);
-      goto clear_pipe;
-    }
-
-  req->unique = in->unique;
-  req->ctx.uid = in->uid;
-  req->ctx.gid = in->gid;
-  req->ctx.pid = in->pid;
-  req->ch = ch;
-
-  err = EIO;
-  if(!f->got_init)
-    {
-      enum fuse_opcode expected;
-
-      expected = FUSE_INIT;
-      if (in->opcode != expected)
-        goto reply_err;
-    }
-  else if(in->opcode == FUSE_INIT)
-    {
-      goto reply_err;
-    }
-
-  err = ENOSYS;
-  if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func)
-    goto reply_err;
-
-  if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size &&
-      (in->opcode != FUSE_WRITE || !f->op.write_buf) &&
-      in->opcode != FUSE_NOTIFY_REPLY)
-    {
-      void *newmbuf;
-
-      err = ENOMEM;
-      newmbuf = realloc(mbuf, buf->size);
-      if (newmbuf == NULL)
-        goto reply_err;
-      mbuf = newmbuf;
-
-      tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size);
-      tmpbuf.buf[0].mem = mbuf + write_header_size;
-
-      res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv);
-      err = -res;
-      if (res < 0)
-        goto reply_err;
-
-      in = mbuf;
-    }
-
-  inarg = (void *) &in[1];
-  if (in->opcode == FUSE_WRITE)
-    do_write_buf(req, in->nodeid, inarg, buf);
-  else if (in->opcode == FUSE_NOTIFY_REPLY)
-    do_notify_reply(req, in->nodeid, inarg, buf);
-  else
-    fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg);
-
- out_free:
-  free(mbuf);
-  return;
-
- reply_err:
-  fuse_reply_err(req, err);
- clear_pipe:
-  if (buf->flags & FUSE_BUF_IS_FD)
-    fuse_ll_clear_pipe(f);
-  goto out_free;
-}
-
-static
-void
-fuse_ll_process(void             *data,
-                const char       *buf,
-                size_t            len,
-                struct fuse_chan *ch)
-{
-  struct fuse_buf fbuf = {
-                          .mem = (void *) buf,
-                          .size = len,
-  };
-
-  fuse_ll_process_buf(data, &fbuf, ch);
-}
-
 enum {
-      KEY_HELP,
-      KEY_VERSION,
+  KEY_HELP,
+  KEY_VERSION,
 };
 
 static const struct fuse_opt fuse_ll_opts[] =
   {
-   { "debug", offsetof(struct fuse_ll, debug), 1 },
-   { "-d", offsetof(struct fuse_ll, debug), 1 },
-   { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
-   { "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
-   { "congestion_threshold=%u",
-     offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
-   { "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
-   { "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
-   { "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
-   { "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
-   { "splice_write", offsetof(struct fuse_ll, splice_write), 1},
-   { "no_splice_write", offsetof(struct fuse_ll, no_splice_write), 1},
-   { "splice_move", offsetof(struct fuse_ll, splice_move), 1},
-   { "no_splice_move", offsetof(struct fuse_ll, no_splice_move), 1},
-   { "splice_read", offsetof(struct fuse_ll, splice_read), 1},
-   { "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
-   FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
-   FUSE_OPT_KEY("-h", KEY_HELP),
-   FUSE_OPT_KEY("--help", KEY_HELP),
-   FUSE_OPT_KEY("-V", KEY_VERSION),
-   FUSE_OPT_KEY("--version", KEY_VERSION),
-   FUSE_OPT_END
+    { "debug", offsetof(struct fuse_ll, debug), 1 },
+    { "-d", offsetof(struct fuse_ll, debug), 1 },
+    { "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
+    { "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
+    { "congestion_threshold=%u",
+      offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
+    { "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+    { "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
+    { "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
+    { "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
+    { "splice_write", offsetof(struct fuse_ll, splice_write), 1},
+    { "no_splice_write", offsetof(struct fuse_ll, no_splice_write), 1},
+    { "splice_move", offsetof(struct fuse_ll, splice_move), 1},
+    { "no_splice_move", offsetof(struct fuse_ll, no_splice_move), 1},
+    { "splice_read", offsetof(struct fuse_ll, splice_read), 1},
+    { "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
+    FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
+    FUSE_OPT_KEY("-h", KEY_HELP),
+    FUSE_OPT_KEY("--help", KEY_HELP),
+    FUSE_OPT_KEY("-V", KEY_VERSION),
+    FUSE_OPT_KEY("--version", KEY_VERSION),
+    FUSE_OPT_END
   };
 
 static
@@ -2616,17 +2043,17 @@ static
 void
 fuse_ll_destroy(void *data)
 {
-  struct fuse_ll *f = (struct fuse_ll *) data;
+  struct fuse_ll *f = (struct fuse_ll *)data;
   struct fuse_ll_pipe *llp;
 
-  if (f->got_init && !f->got_destroy)
+  if(f->got_init && !f->got_destroy)
     {
-      if (f->op.destroy)
+      if(f->op.destroy)
         f->op.destroy(f->userdata);
     }
 
   llp = pthread_getspecific(f->pipe_key);
-  if (llp != NULL)
+  if(llp != NULL)
     fuse_ll_pipe_free(llp);
   pthread_key_delete(f->pipe_key);
   pthread_mutex_destroy(&f->lock);
@@ -2643,136 +2070,270 @@ fuse_ll_pipe_destructor(void *data)
   fuse_ll_pipe_free(llp);
 }
 
-#ifdef HAVE_SPLICE
 static
-int
-fuse_ll_receive_buf(struct fuse_session  *se,
-                    struct fuse_buf      *buf,
-                    struct fuse_chan    **chp)
+void
+fuse_send_errno(struct fuse_ll   *f_,
+                struct fuse_chan *ch_,
+                const int         errno_,
+                const uint64_t    unique_id_)
 {
-  struct fuse_chan *ch = *chp;
-  struct fuse_ll *f = fuse_session_data(se);
-  size_t bufsize = buf->size;
-  struct fuse_ll_pipe *llp;
-  struct fuse_buf tmpbuf;
-  int err;
-  int res;
+  struct fuse_out_header out = {0};
+  struct iovec           iov = {0};
 
-  if (f->conn.proto_minor < 14 || !(f->conn.want & FUSE_CAP_SPLICE_READ))
-    goto fallback;
+  out.unique   = unique_id_;
+  out.error    = -errno_;
+  iov.iov_base = &out;
+  iov.iov_len  = sizeof(struct fuse_out_header);
 
-  llp = fuse_ll_get_pipe(f);
-  if (llp == NULL)
-    goto fallback;
+  fuse_send_msg(f_,ch_,&iov,1);
+}
 
-  if (llp->size < bufsize)
+static
+void
+fuse_send_enomem(struct fuse_ll   *f_,
+                 struct fuse_chan *ch_,
+                 const uint64_t    unique_id_)
+{
+  fuse_send_errno(f_,ch_,ENOMEM,unique_id_);
+}
+
+static
+int
+fuse_ll_buf_receive_read(struct fuse_session *se_,
+                         fuse_msgbuf_t       *msgbuf_)
+{
+  int rv;
+
+  rv = read(fuse_chan_fd(se_->ch),msgbuf_->mem,msgbuf_->size);
+  if(rv == -1)
+    return -errno;
+
+  if(rv < sizeof(struct fuse_in_header))
     {
-      if (llp->can_grow)
-        {
-          res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize);
-          if (res == -1)
-            {
-              llp->can_grow = 0;
-              goto fallback;
-            }
-          llp->size = res;
-        }
-      if (llp->size < bufsize)
-        goto fallback;
+      fprintf(stderr, "short splice from fuse device\n");
+      return -EIO;
     }
 
-  res = splice(fuse_chan_fd(ch), NULL, llp->pipe[1], NULL, bufsize, 0);
-  err = errno;
+  return rv;
+}
 
-  if(fuse_session_exited(se))
-    return 0;
+static
+void
+fuse_ll_buf_process_read(struct fuse_session *se_,
+                         const fuse_msgbuf_t *msgbuf_)
+{
+  int err;
+  struct fuse_req *req;
+  struct fuse_in_header *in;
 
-  if (res == -1)
-    {
-      if (err == ENODEV)
-        {
-          fuse_session_exit(se);
-          return 0;
-        }
+  in = (struct fuse_in_header*)msgbuf_->mem;
 
-      if (err != EINTR && err != EAGAIN)
-        perror("fuse: splice from device");
+  req = fuse_ll_alloc_req(se_->f);
+  if(req == NULL)
+    return fuse_send_enomem(se_->f,se_->ch,in->unique);
 
-      return -err;
-    }
+  req->unique  = in->unique;
+  req->ctx.uid = in->uid;
+  req->ctx.gid = in->gid;
+  req->ctx.pid = in->pid;
+  req->ch      = se_->ch;
+
+  err = ENOSYS;
+  if(in->opcode >= FUSE_MAXOP)
+    goto reply_err;
+  if(fuse_ll_ops[in->opcode].func == NULL)
+    goto reply_err;
+
+  fuse_ll_ops[in->opcode].func(req, in);
+
+  return;
+
+ reply_err:
+  fuse_reply_err(req, err);
+  return;
+}
 
-  if (res < sizeof(struct fuse_in_header))
+static
+void
+fuse_ll_buf_process_read_init(struct fuse_session *se_,
+                              const fuse_msgbuf_t *msgbuf_)
+{
+  int err;
+  struct fuse_req *req;
+  struct fuse_in_header *in;
+
+  in = (struct fuse_in_header*)msgbuf_->mem;
+
+  req = fuse_ll_alloc_req(se_->f);
+  if(req == NULL)
+    return fuse_send_enomem(se_->f,se_->ch,in->unique);
+
+  req->unique  = in->unique;
+  req->ctx.uid = in->uid;
+  req->ctx.gid = in->gid;
+  req->ctx.pid = in->pid;
+  req->ch      = se_->ch;
+
+  err = EIO;
+  if(in->opcode != FUSE_INIT)
+    goto reply_err;
+  if(fuse_ll_ops[in->opcode].func == NULL)
+    goto reply_err;
+
+  se_->process_buf = fuse_ll_buf_process_read;
+
+  fuse_ll_ops[in->opcode].func(req, in);
+
+  return;
+
+ reply_err:
+  fuse_reply_err(req, err);
+  return;
+}
+
+
+#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE)
+static
+int
+fuse_ll_buf_receive_splice(struct fuse_session *se_,
+                           fuse_msgbuf_t       *msgbuf_)
+{
+  int rv;
+  size_t bufsize = msgbuf_->size;
+
+  rv = splice(fuse_chan_fd(se_->ch),NULL,msgbuf_->pipefd[1],NULL,bufsize,SPLICE_F_MOVE);
+  if(rv == -1)
+    return -errno;
+
+  if(rv < sizeof(struct fuse_in_header))
     {
-      fprintf(stderr, "short splice from fuse device\n");
+      fprintf(stderr,"short splice from fuse device\n");
       return -EIO;
     }
 
-  tmpbuf = (struct fuse_buf) {
-                              .size = res,
-                              .flags = FUSE_BUF_IS_FD,
-                              .fd = llp->pipe[0],
-  };
+  return rv;
+}
 
-  /*
-   * Don't bother with zero copy for small requests.
-   * fuse_loop_mt() needs to check for FORGET so this more than
-   * just an optimization.
-   */
-  if (res < sizeof(struct fuse_in_header) +
-      sizeof(struct fuse_write_in) + pagesize)
+static
+void
+fuse_ll_buf_process_splice(struct fuse_session *se_,
+                           const fuse_msgbuf_t *msgbuf_)
+{
+  int rv;
+  struct fuse_req *req;
+  struct fuse_in_header *in;
+  struct iovec iov = { msgbuf_->mem, msgbuf_->size };
+
+ retry:
+  rv = vmsplice(msgbuf_->pipefd[0], &iov, 1, 0);
+  if(rv == -1)
     {
-      struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 };
-      struct fuse_bufvec dst = { .buf[0] = *buf, .count = 1 };
-
-      res = fuse_buf_copy(&dst, &src, 0);
-      if (res < 0) {
-        fprintf(stderr, "fuse: copy from pipe: %s\n",
-                strerror(-res));
-        fuse_ll_clear_pipe(f);
-        return res;
-      }
-
-      if (res < tmpbuf.size)
-        {
-          fprintf(stderr, "fuse: copy from pipe: short read\n");
-          fuse_ll_clear_pipe(f);
-          return -EIO;
-        }
+      rv = errno;
+      if(rv == EAGAIN)
+        goto retry;
+      // TODO: Need to propagate back errors to caller
+      return;
+    }
+
+  in = (struct fuse_in_header*)msgbuf_->mem;
+
+  req = fuse_ll_alloc_req(se_->f);
+  if(req == NULL)
+    return fuse_send_enomem(se_->f,se_->ch,in->unique);
 
-      buf->size = tmpbuf.size;
+  req->unique  = in->unique;
+  req->ctx.uid = in->uid;
+  req->ctx.gid = in->gid;
+  req->ctx.pid = in->pid;
+  req->ch      = se_->ch;
 
-      return buf->size;
+  rv = ENOSYS;
+  if(in->opcode >= FUSE_MAXOP)
+    goto reply_err;
+  if(fuse_ll_ops[in->opcode].func == NULL)
+    goto reply_err;
+
+  fuse_ll_ops[in->opcode].func(req, in);
+
+  return;
+
+ reply_err:
+  fuse_reply_err(req, rv);
+  return;
+}
+
+static
+void
+fuse_ll_buf_process_splice_init(struct fuse_session *se_,
+                                const fuse_msgbuf_t *msgbuf_)
+{
+  int rv;
+  struct fuse_req *req;
+  struct fuse_in_header *in;
+  struct iovec iov = { msgbuf_->mem, msgbuf_->size };
+
+ retry:
+  rv = vmsplice(msgbuf_->pipefd[0], &iov, 1, 0);
+  if(rv == -1)
+    {
+      rv = errno;
+      if(rv == EAGAIN)
+        goto retry;
+      // TODO: Need to propagate back errors to caller
+      return;
     }
 
-  *buf = tmpbuf;
+  in = (struct fuse_in_header*)msgbuf_->mem;
 
-  return res;
+  req = fuse_ll_alloc_req(se_->f);
+  if(req == NULL)
+    return fuse_send_enomem(se_->f,se_->ch,in->unique);
 
- fallback:
-  res = fuse_chan_recv(chp, buf->mem, bufsize);
-  if (res <= 0)
-    return res;
+  req->unique  = in->unique;
+  req->ctx.uid = in->uid;
+  req->ctx.gid = in->gid;
+  req->ctx.pid = in->pid;
+  req->ch      = se_->ch;
+
+  rv = EIO;
+  if(in->opcode != FUSE_INIT)
+    goto reply_err;
+  if(fuse_ll_ops[in->opcode].func == NULL)
+    goto reply_err;
 
-  buf->size = res;
+  se_->process_buf = fuse_ll_buf_process_splice;
 
-  return res;
+  fuse_ll_ops[in->opcode].func(req, in);
+
+  return;
+
+ reply_err:
+  fuse_reply_err(req, rv);
+  return;
 }
 #else
 static
 int
-fuse_ll_receive_buf(struct fuse_session  *se,
-                    struct fuse_buf      *buf,
-                    struct fuse_chan    **chp)
+fuse_ll_buf_receive_splice(struct fuse_session *se_,
+                           fuse_msgbuf_t       *msgbuf_)
 {
-  (void) se;
-
-  int res = fuse_chan_recv(chp, buf->mem, buf->size);
-  if (res <= 0)
-    return res;
+  return fuse_ll_buf_receive_read(se_,msgbuf_);
+}
 
-  buf->size = res;
+static
+void
+fuse_ll_buf_process_splice(struct fuse_session *se_,
+                           const fuse_msgbuf_t *msgbuf_)
+{
+  return fuse_ll_buf_process_read(se_,msgbuf_);
+}
 
-  return res;
+static
+void
+fuse_ll_buf_process_splice_init(struct fuse_session *se_,
+                                const fuse_msgbuf_t *msgbuf_)
+{
+  return fuse_ll_buf_process_read_init(se_,msgbuf_);
 }
 #endif
 
@@ -2790,19 +2351,15 @@ fuse_lowlevel_new_common(struct fuse_args               *args,
   int err;
   struct fuse_ll *f;
   struct fuse_session *se;
-  struct fuse_session_ops sop = {
-                                 .process = fuse_ll_process,
-                                 .destroy = fuse_ll_destroy,
-  };
 
-  if (sizeof(struct fuse_lowlevel_ops) < op_size)
+  if(sizeof(struct fuse_lowlevel_ops) < op_size)
     {
       fprintf(stderr, "fuse: warning: library too old, some operations may not work\n");
       op_size = sizeof(struct fuse_lowlevel_ops);
     }
 
   f = (struct fuse_ll *) calloc(1, sizeof(struct fuse_ll));
-  if (f == NULL)
+  if(f == NULL)
     {
       fprintf(stderr, "fuse: failed to allocate fuse object\n");
       goto out;
@@ -2815,26 +2372,37 @@ fuse_lowlevel_new_common(struct fuse_args               *args,
   fuse_mutex_init(&f->lock);
 
   err = pthread_key_create(&f->pipe_key, fuse_ll_pipe_destructor);
-  if (err)
+  if(err)
     {
       fprintf(stderr, "fuse: failed to create thread specific key: %s\n",
               strerror(err));
       goto out_free;
     }
 
-  if (fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
+  if(fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
     goto out_key_destroy;
 
   memcpy(&f->op, op, op_size);
   f->owner = getuid();
   f->userdata = userdata;
 
-  se = fuse_session_new(&sop, f);
-  if (!se)
-    goto out_key_destroy;
+  if(f->splice_read)
+    {
+      se = fuse_session_new(f,
+                            fuse_ll_buf_receive_splice,
+                            fuse_ll_buf_process_splice_init,
+                            fuse_ll_destroy);
+    }
+  else
+    {
+      se = fuse_session_new(f,
+                            fuse_ll_buf_receive_read,
+                            fuse_ll_buf_process_read_init,
+                            fuse_ll_destroy);
+    }
 
-  se->receive_buf = fuse_ll_receive_buf;
-  se->process_buf = fuse_ll_process_buf;
+  if(!se)
+    goto out_key_destroy;
 
   return se;
 
diff --git a/libfuse/lib/fuse_msgbuf.cpp b/libfuse/lib/fuse_msgbuf.cpp
new file mode 100644
index 0000000..0c29bc9
--- /dev/null
+++ b/libfuse/lib/fuse_msgbuf.cpp
@@ -0,0 +1,123 @@
+/*
+  ISC License
+
+  Copyright (c) 2022, Antonio SJ Musumeci <trapexit@spawn.link>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "fuse_msgbuf.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <mutex>
+#include <stack>
+
+
+static std::size_t g_BUFSIZE = (1024 * 1024 * 2);
+
+static std::mutex g_MUTEX;
+static std::stack<fuse_msgbuf_t*> g_MSGBUF_STACK;
+
+static
+__attribute__((destructor))
+void
+msgbuf_destroy()
+{
+  // TODO: cleanup?
+}
+
+std::size_t
+msgbuf_bufsize()
+{
+  return g_BUFSIZE;
+}
+
+void
+msgbuf_bufsize(const std::size_t size_)
+{
+  g_BUFSIZE = size_;
+}
+
+fuse_msgbuf_t*
+msgbuf_alloc()
+{
+  int rv;
+  fuse_msgbuf_t *msgbuf;
+
+  g_MUTEX.lock();
+  if(g_MSGBUF_STACK.empty())
+    {
+      g_MUTEX.unlock();
+
+      msgbuf = (fuse_msgbuf_t*)malloc(sizeof(fuse_msgbuf_t));
+      if(msgbuf == NULL)
+        return NULL;
+
+      rv = pipe(msgbuf->pipefd);
+      assert(rv == 0);
+      rv = fcntl(msgbuf->pipefd[0],F_SETPIPE_SZ,g_BUFSIZE);
+      assert(rv > 0);
+      msgbuf->mem  = (char*)malloc(rv);
+      msgbuf->size = rv;
+      msgbuf->used = 0;
+    }
+  else
+    {
+      msgbuf = g_MSGBUF_STACK.top();
+      g_MSGBUF_STACK.pop();
+      g_MUTEX.unlock();
+    }
+
+  return msgbuf;
+}
+
+fuse_msgbuf_t*
+msgbuf_alloc_memonly()
+{
+  fuse_msgbuf_t *msgbuf;
+
+  g_MUTEX.lock();
+  if(g_MSGBUF_STACK.empty())
+    {
+      g_MUTEX.unlock();
+
+      msgbuf = (fuse_msgbuf_t*)malloc(sizeof(fuse_msgbuf_t));
+      if(msgbuf == NULL)
+        return NULL;
+
+      msgbuf->pipefd[0] = -1;
+      msgbuf->pipefd[1] = -1;
+      msgbuf->mem  = (char*)malloc(g_BUFSIZE);
+      msgbuf->size = g_BUFSIZE;
+      msgbuf->used = 0;
+    }
+  else
+    {
+      msgbuf = g_MSGBUF_STACK.top();
+      g_MSGBUF_STACK.pop();
+      g_MUTEX.unlock();
+    }
+
+  return msgbuf;
+}
+
+void
+msgbuf_free(fuse_msgbuf_t *msgbuf_)
+{
+  std::lock_guard<std::mutex> lck(g_MUTEX);
+
+  g_MSGBUF_STACK.push(msgbuf_);
+}
diff --git a/libfuse/lib/fuse_msgbuf.hpp b/libfuse/lib/fuse_msgbuf.hpp
new file mode 100644
index 0000000..2579131
--- /dev/null
+++ b/libfuse/lib/fuse_msgbuf.hpp
@@ -0,0 +1,29 @@
+/*
+  ISC License
+
+  Copyright (c) 2022, Antonio SJ Musumeci <trapexit@spawn.link>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#pragma once
+
+#include "fuse_msgbuf.h"
+
+void           msgbuf_bufsize(const uint32_t size);
+std::size_t    msgbuf_bufsize();
+
+fuse_msgbuf_t* msgbuf_alloc();
+fuse_msgbuf_t* msgbuf_alloc_memonly();
+
+void           msgbuf_free(fuse_msgbuf_t *msgbuf);
diff --git a/libfuse/lib/fuse_mt.c b/libfuse/lib/fuse_mt.c
index 1de419e..1752bef 100644
--- a/libfuse/lib/fuse_mt.c
+++ b/libfuse/lib/fuse_mt.c
@@ -16,98 +16,8 @@
 #include <pthread.h>
 #include <assert.h>
 
-struct procdata
-{
-  struct fuse *f;
-  struct fuse_chan *prevch;
-  struct fuse_session *prevse;
-  fuse_processor_t proc;
-  void *data;
-};
-
-static void mt_session_proc(void *data, const char *buf, size_t len,
-			    struct fuse_chan *ch)
-{
-  struct procdata *pd = (struct procdata *) data;
-  struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
-
-  (void) len;
-  (void) ch;
-  pd->proc(pd->f, cmd, pd->data);
-}
-
-static void mt_session_exit(void *data, int val)
-{
-  struct procdata *pd = (struct procdata *) data;
-  if (val)
-    fuse_session_exit(pd->prevse);
-  else
-    fuse_session_reset(pd->prevse);
-}
-
-static int mt_session_exited(void *data)
-{
-  struct procdata *pd = (struct procdata *) data;
-  return fuse_session_exited(pd->prevse);
-}
-
-static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
-{
-  struct fuse_cmd *cmd;
-  struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
-
-  assert(size >= sizeof(cmd));
-
-  cmd = fuse_read_cmd(pd->f);
-  if (cmd == NULL)
-    return 0;
-
-  *(struct fuse_cmd **)buf = cmd;
-
-  return sizeof(cmd);
-}
-
-int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
-{
-  int res;
-  struct procdata pd;
-  struct fuse_session *prevse = fuse_get_session(f);
-  struct fuse_session *se;
-  struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
-  struct fuse_chan *ch;
-  struct fuse_session_ops sop = {
-    .exit = mt_session_exit,
-    .exited = mt_session_exited,
-    .process = mt_session_proc,
-  };
-  struct fuse_chan_ops cop = {
-    .receive = mt_chan_receive,
-  };
-
-  pd.f = f;
-  pd.prevch = prevch;
-  pd.prevse = prevse;
-  pd.proc = proc;
-  pd.data = data;
-
-  se = fuse_session_new(&sop, &pd);
-  if (se == NULL)
-    return -1;
-
-  ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
-                     sizeof(struct fuse_cmd *), &pd);
-  if (ch == NULL) {
-    fuse_session_destroy(se);
-    return -1;
-  }
-  fuse_session_add_chan(se, ch);
-  res = fuse_session_loop_mt(se,
-                             fuse_config_num_threads(f));
-  fuse_session_destroy(se);
-  return res;
-}
-
-int fuse_loop_mt(struct fuse *f)
+int
+fuse_loop_mt(struct fuse *f)
 {
   if (f == NULL)
     return -1;
@@ -117,7 +27,8 @@ int fuse_loop_mt(struct fuse *f)
     return -1;
 
   res = fuse_session_loop_mt(fuse_get_session(f),
-                             fuse_config_num_threads(f));
+                             fuse_config_read_thread_count(f),
+                             fuse_config_process_thread_count(f));
 
   fuse_stop_maintenance_thread(f);
 
diff --git a/libfuse/lib/fuse_session.c b/libfuse/lib/fuse_session.c
index ea6eacc..6a7cc0c 100644
--- a/libfuse/lib/fuse_session.c
+++ b/libfuse/lib/fuse_session.c
@@ -8,23 +8,29 @@
 
 #include "fuse_i.h"
 #include "fuse_misc.h"
+#include "fuse_kernel.h"
 
+#include <assert.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
-#include <errno.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
 
 struct fuse_chan
 {
-  struct fuse_chan_ops op;
   struct fuse_session *se;
   int fd;
   size_t bufsize;
-  void *data;
 };
 
-struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
+struct fuse_session *
+fuse_session_new(void *data,
+                 void *receive_buf,
+                 void *process_buf,
+                 void *destroy)
 {
   struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
   if (se == NULL) {
@@ -33,8 +39,10 @@ struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
   }
 
   memset(se, 0, sizeof(*se));
-  se->op = *op;
-  se->data = data;
+  se->f           = data;
+  se->receive_buf = receive_buf;
+  se->process_buf = process_buf;
+  se->destroy     = destroy;
 
   return se;
 }
@@ -57,110 +65,59 @@ void fuse_session_remove_chan(struct fuse_chan *ch)
   }
 }
 
-struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
-					 struct fuse_chan *ch)
-{
-  assert(ch == NULL || ch == se->ch);
-  if (ch == NULL)
-    return se->ch;
-  else
-    return NULL;
-}
-
-void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
-			  struct fuse_chan *ch)
-{
-  se->op.process(se->data, buf, len, ch);
-}
-
-void fuse_session_process_buf(struct fuse_session *se,
-			      const struct fuse_buf *buf, struct fuse_chan *ch)
-{
-  if (se->process_buf) {
-    se->process_buf(se->data, buf, ch);
-  } else {
-    assert(!(buf->flags & FUSE_BUF_IS_FD));
-    fuse_session_process(se->data, buf->mem, buf->size, ch);
-  }
-}
-
-int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
-			     struct fuse_chan **chp)
-{
-  int res;
-
-  if (se->receive_buf) {
-    res = se->receive_buf(se, buf, chp);
-  } else {
-    res = fuse_chan_recv(chp, buf->mem, buf->size);
-    if (res > 0)
-      buf->size = res;
-  }
-
-  return res;
-}
-
-
-void fuse_session_destroy(struct fuse_session *se)
+void
+fuse_session_destroy(struct fuse_session *se)
 {
-  if (se->op.destroy)
-    se->op.destroy(se->data);
-  if (se->ch != NULL)
+  se->destroy(se->f);
+  if(se->ch != NULL)
     fuse_chan_destroy(se->ch);
   free(se);
 }
 
-void fuse_session_exit(struct fuse_session *se)
+void fuse_session_reset(struct fuse_session *se)
 {
-  if (se->op.exit)
-    se->op.exit(se->data, 1);
-  se->exited = 1;
+  se->exited = 0;
 }
 
-void fuse_session_reset(struct fuse_session *se)
+int
+fuse_session_exited(struct fuse_session *se)
 {
-  if (se->op.exit)
-    se->op.exit(se->data, 0);
-  se->exited = 0;
+  return se->exited;
 }
 
-int fuse_session_exited(struct fuse_session *se)
+void
+fuse_session_exit(struct fuse_session *se_)
 {
-  if (se->op.exited)
-    return se->op.exited(se->data);
-  else
-    return se->exited;
+  se_->exited = 1;
 }
 
-void *fuse_session_data(struct fuse_session *se)
+void*
+fuse_session_data(struct fuse_session *se)
 {
-  return se->data;
+  return se->f;
 }
 
-static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
-					      size_t bufsize, void *data)
+struct fuse_chan *
+fuse_chan_new(int fd,
+              size_t bufsize)
 {
-  struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
-  if (ch == NULL) {
-    fprintf(stderr, "fuse: failed to allocate channel\n");
-    return NULL;
-  }
+  struct fuse_chan *ch;
+
+  ch = (struct fuse_chan*)malloc(sizeof(*ch));
+  if(ch == NULL)
+    {
+      fprintf(stderr, "fuse: failed to allocate channel\n");
+      return NULL;
+    }
 
   memset(ch, 0, sizeof(*ch));
-  ch->op = *op;
+
   ch->fd = fd;
   ch->bufsize = bufsize;
-  ch->data = data;
 
   return ch;
 }
 
-struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
-				size_t bufsize, void *data)
-{
-  return fuse_chan_new_common(op, fd, bufsize, data);
-}
-
 int fuse_chan_fd(struct fuse_chan *ch)
 {
   return ch->fd;
@@ -178,40 +135,21 @@ size_t fuse_chan_bufsize(struct fuse_chan *ch)
   return ch->bufsize;
 }
 
-void *fuse_chan_data(struct fuse_chan *ch)
-{
-  return ch->data;
-}
-
 struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
 {
   return ch->se;
 }
 
-int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
+void
+fuse_chan_destroy(struct fuse_chan *ch)
 {
-  struct fuse_chan *ch = *chp;
-
-  return ch->op.receive(chp, buf, size);
-}
-
-int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
-{
-  int res;
+  int fd;
 
-  res = fuse_chan_recv(&ch, buf, size);
-  return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
-}
+  fuse_session_remove_chan(ch);
 
-int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
-{
-  return ch->op.send(ch, iov, count);
-}
+  fd = fuse_chan_fd(ch);
+  if(fd != -1)
+    close(fd);
 
-void fuse_chan_destroy(struct fuse_chan *ch)
-{
-  fuse_session_remove_chan(ch);
-  if (ch->op.destroy)
-    ch->op.destroy(ch);
   free(ch);
 }
diff --git a/libfuse/lib/helper.c b/libfuse/lib/helper.c
index 8dd48ad..2e9b9fe 100644
--- a/libfuse/lib/helper.c
+++ b/libfuse/lib/helper.c
@@ -237,8 +237,10 @@ struct fuse_chan *
 fuse_mount_common(const char       *mountpoint_,
                   struct fuse_args *args_)
 {
-  struct fuse_chan *ch;
   int fd;
+  long bufsize;
+  long pagesize;
+  struct fuse_chan *ch;
 
   /*
    * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
@@ -255,7 +257,10 @@ fuse_mount_common(const char       *mountpoint_,
   if(fd == -1)
     return NULL;
 
-  ch = fuse_kern_chan_new(fd);
+  pagesize = sysconf(_SC_PAGESIZE);
+  bufsize  = ((FUSE_MAX_MAX_PAGES + 1) * pagesize);
+
+  ch = fuse_chan_new(fd,bufsize);
   if(!ch)
     fuse_kern_unmount(mountpoint_, fd);
 
@@ -284,11 +289,13 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
   fuse_unmount_common(mountpoint, ch);
 }
 
-struct fuse *fuse_setup_common(int argc, char *argv[],
-			       const struct fuse_operations *op,
-			       size_t op_size,
-			       char **mountpoint,
-			       int *fd)
+struct fuse *
+fuse_setup_common(int argc,
+                  char *argv[],
+                  const struct fuse_operations *op,
+                  size_t op_size,
+                  char **mountpoint,
+                  int *fd)
 {
   struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
   struct fuse_chan *ch;
@@ -344,7 +351,7 @@ struct fuse *fuse_setup(int argc, char *argv[],
 static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
 {
   struct fuse_session *se = fuse_get_session(fuse);
-  struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
+  struct fuse_chan *ch = se->ch;
   fuse_remove_signal_handlers(se);
   fuse_unmount_common(mountpoint, ch);
   fuse_destroy(fuse);
diff --git a/libfuse/lib/mount_bsd.c b/libfuse/lib/mount_bsd.c
index bc17bed..b1a5457 100644
--- a/libfuse/lib/mount_bsd.c
+++ b/libfuse/lib/mount_bsd.c
@@ -90,7 +90,6 @@ static const struct fuse_opt fuse_mount_opts[] = {
    * handle them
    */
   FUSE_OPT_KEY("fsname=",			KEY_KERN),
-  FUSE_OPT_KEY("nonempty",		KEY_KERN),
   FUSE_OPT_KEY("large_read",		KEY_KERN),
   FUSE_OPT_END
 };
diff --git a/libfuse/lib/mount_generic.c b/libfuse/lib/mount_generic.c
index 807f580..7b98f73 100644
--- a/libfuse/lib/mount_generic.c
+++ b/libfuse/lib/mount_generic.c
@@ -64,7 +64,6 @@ struct mount_opts {
   int allow_other;
   int ishelp;
   int flags;
-  int nonempty;
   int auto_unmount;
   int blkdev;
   char *fsname;
@@ -79,13 +78,11 @@ struct mount_opts {
 
 static const struct fuse_opt fuse_mount_opts[] = {
   FUSE_MOUNT_OPT("allow_other",		allow_other),
-  FUSE_MOUNT_OPT("nonempty",		nonempty),
   FUSE_MOUNT_OPT("blkdev",		blkdev),
   FUSE_MOUNT_OPT("auto_unmount",		auto_unmount),
   FUSE_MOUNT_OPT("fsname=%s",		fsname),
   FUSE_MOUNT_OPT("subtype=%s",		subtype),
   FUSE_OPT_KEY("allow_other",		KEY_KERN_OPT),
-  FUSE_OPT_KEY("nonempty",		KEY_FUSERMOUNT_OPT),
   FUSE_OPT_KEY("auto_unmount",		KEY_FUSERMOUNT_OPT),
   FUSE_OPT_KEY("blkdev",			KEY_FUSERMOUNT_OPT),
   FUSE_OPT_KEY("fsname=",			KEY_FUSERMOUNT_OPT),
@@ -126,7 +123,6 @@ static void mount_help(void)
   fprintf(stderr,
           "    -o allow_other         allow access to other users\n"
           "    -o auto_unmount        auto unmount on process termination\n"
-          "    -o nonempty            allow mounts over non-empty file/dir\n"
           "    -o default_permissions enable permission checking by kernel\n"
           "    -o fsname=NAME         set filesystem name\n"
           "    -o subtype=NAME        set filesystem type\n"
@@ -424,13 +420,6 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
     return -1;
   }
 
-  if (!mo->nonempty) {
-    res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
-                               stbuf.st_size);
-    if (res == -1)
-      return -1;
-  }
-
   if (mo->auto_unmount) {
     /* Tell the caller to fallback to fusermount because
        auto-unmount does not work otherwise. */
diff --git a/libfuse/lib/mount_util.c b/libfuse/lib/mount_util.c
index b9eefca..afda141 100644
--- a/libfuse/lib/mount_util.c
+++ b/libfuse/lib/mount_util.c
@@ -312,39 +312,6 @@ char *fuse_mnt_resolve_path(const char *progname, const char *orig)
   return dst;
 }
 
-int fuse_mnt_check_empty(const char *progname, const char *mnt,
-			 mode_t rootmode, off_t rootsize)
-{
-  int isempty = 1;
-
-  if (S_ISDIR(rootmode)) {
-    struct dirent *ent;
-    DIR *dp = opendir(mnt);
-    if (dp == NULL) {
-      fprintf(stderr,
-              "%s: failed to open mountpoint for reading: %s\n",
-              progname, strerror(errno));
-      return -1;
-    }
-    while ((ent = readdir(dp)) != NULL) {
-      if (strcmp(ent->d_name, ".") != 0 &&
-          strcmp(ent->d_name, "..") != 0) {
-        isempty = 0;
-        break;
-      }
-    }
-    closedir(dp);
-  } else if (rootsize)
-    isempty = 0;
-
-  if (!isempty) {
-    fprintf(stderr, "%s: mountpoint is not empty\n", progname);
-    fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
-    return -1;
-  }
-  return 0;
-}
-
 int fuse_mnt_check_fuseblk(void)
 {
   char buf[256];
diff --git a/libfuse/lib/mount_util.h b/libfuse/lib/mount_util.h
index bb447fe..5e8b0fb 100644
--- a/libfuse/lib/mount_util.h
+++ b/libfuse/lib/mount_util.h
@@ -16,6 +16,4 @@ int fuse_mnt_remove_mount(const char *progname, const char *mnt);
 int fuse_mnt_umount(const char *progname, const char *abs_mnt,
 		    const char *rel_mnt, int lazy);
 char *fuse_mnt_resolve_path(const char *progname, const char *orig);
-int fuse_mnt_check_empty(const char *progname, const char *mnt,
-			 mode_t rootmode, off_t rootsize);
 int fuse_mnt_check_fuseblk(void);
diff --git a/libfuse/lib/thread_pool.hpp b/libfuse/lib/thread_pool.hpp
new file mode 100644
index 0000000..1599bef
--- /dev/null
+++ b/libfuse/lib/thread_pool.hpp
@@ -0,0 +1,112 @@
+#pragma once
+
+#include "unbounded_queue.hpp"
+
+#include <tuple>
+#include <atomic>
+#include <vector>
+#include <thread>
+#include <memory>
+#include <future>
+#include <utility>
+#include <functional>
+#include <type_traits>
+
+
+class ThreadPool
+{
+public:
+  explicit
+  ThreadPool(const std::size_t thread_count_ = std::thread::hardware_concurrency())
+    : _queues(thread_count_),
+      _count(thread_count_)
+  {
+    auto worker = [this](std::size_t i)
+    {
+      while(true)
+        {
+          Proc f;
+
+          for(std::size_t n = 0; n < (_count * K); ++n)
+            {
+              if(_queues[(i + n) % _count].try_pop(f))
+                break;
+            }
+
+          if(!f && !_queues[i].pop(f))
+            break;
+
+          f();
+        }
+    };
+
+    _threads.reserve(thread_count_);
+    for(std::size_t i = 0; i < thread_count_; ++i)
+      _threads.emplace_back(worker, i);
+  }
+
+  ~ThreadPool()
+  {
+    for(auto& queue : _queues)
+      queue.unblock();
+    for(auto& thread : _threads)
+      thread.join();
+  }
+
+  template<typename F>
+  void
+  enqueue_work(F&& f_)
+  {
+    auto i = _index++;
+
+    for(std::size_t n = 0; n < (_count * K); ++n)
+      {
+        if(_queues[(i + n) % _count].try_push(f_))
+          return;
+      }
+
+    _queues[i % _count].push(std::move(f_));
+  }
+
+  template<typename F>
+  [[nodiscard]]
+  std::future<typename std::result_of<F()>::type>
+  enqueue_task(F&& f_)
+  {
+    using TaskReturnType = typename std::result_of<F()>::type;
+    using Promise        = std::promise<TaskReturnType>;
+
+    auto i       = _index++;
+    auto promise = std::make_shared<Promise>();
+    auto future  = promise->get_future();
+    auto work    = [=]() {
+      auto rv = f_();
+      promise->set_value(rv);
+    };
+
+    for(std::size_t n = 0; n < (_count * K); ++n)
+      {
+        if(_queues[(i + n) % _count].try_push(work))
+          return future;
+      }
+
+    _queues[i % _count].push(std::move(work));
+
+    return future;
+  }
+
+private:
+  using Proc   = std::function<void(void)>;
+  using Queue  = UnboundedQueue<Proc>;
+  using Queues = std::vector<Queue>;
+  Queues _queues;
+
+private:
+  std::vector<std::thread> _threads;
+
+private:
+  const std::size_t _count;
+  std::atomic_uint _index;
+
+  static const unsigned int K = 2;
+};
diff --git a/libfuse/lib/unbounded_queue.hpp b/libfuse/lib/unbounded_queue.hpp
new file mode 100644
index 0000000..1e527b7
--- /dev/null
+++ b/libfuse/lib/unbounded_queue.hpp
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <utility>
+
+
+template<typename T>
+class UnboundedQueue
+{
+public:
+  explicit
+  UnboundedQueue(bool block_ = true)
+    : _block(block_)
+  {
+  }
+
+  void
+  push(const T& item_)
+  {
+    {
+      std::lock_guard<std::mutex> guard(_queue_lock);
+      _queue.push(item_);
+    }
+    _condition.notify_one();
+  }
+
+  void
+  push(T&& item_)
+  {
+    {
+      std::lock_guard<std::mutex> guard(_queue_lock);
+      _queue.push(std::move(item_));
+    }
+
+    _condition.notify_one();
+  }
+
+  template<typename... Args>
+  void
+  emplace(Args&&... args_)
+  {
+    {
+      std::lock_guard<std::mutex> guard(_queue_lock);
+      _queue.emplace(std::forward<Args>(args_)...);
+    }
+
+    _condition.notify_one();
+  }
+
+  bool
+  try_push(const T& item_)
+  {
+    {
+      std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock);
+      if(!lock)
+        return false;
+      _queue.push(item_);
+    }
+
+    _condition.notify_one();
+
+    return true;
+  }
+
+  bool
+  try_push(T&& item_)
+  {
+    {
+      std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock);
+      if(!lock)
+        return false;
+      _queue.push(std::move(item_));
+    }
+
+    _condition.notify_one();
+
+    return true;
+  }
+
+  //TODO: push multiple T at once
+
+  bool
+  pop(T& item_)
+  {
+    std::unique_lock<std::mutex> guard(_queue_lock);
+
+    _condition.wait(guard, [&]() { return !_queue.empty() || !_block; });
+    if(_queue.empty())
+      return false;
+
+    item_ = std::move(_queue.front());
+    _queue.pop();
+
+    return true;
+  }
+
+  bool
+  try_pop(T& item_)
+  {
+    std::unique_lock<std::mutex> lock(_queue_lock, std::try_to_lock);
+    if(!lock || _queue.empty())
+      return false;
+
+    item_ = std::move(_queue.front());
+    _queue.pop();
+
+    return true;
+  }
+
+  std::size_t
+  size() const
+  {
+    std::lock_guard<std::mutex> guard(_queue_lock);
+
+    return _queue.size();
+  }
+
+  bool
+  empty() const
+  {
+    std::lock_guard<std::mutex> guard(_queue_lock);
+
+    return _queue.empty();
+  }
+
+  void
+  block()
+  {
+    std::lock_guard<std::mutex> guard(_queue_lock);
+    _block = true;
+  }
+
+  void
+  unblock()
+  {
+    {
+      std::lock_guard<std::mutex> guard(_queue_lock);
+      _block = false;
+    }
+
+    _condition.notify_all();
+  }
+
+  bool
+  blocking() const
+  {
+    std::lock_guard<std::mutex> guard(_queue_lock);
+
+    return _block;
+  }
+
+private:
+  mutable std::mutex _queue_lock;
+
+private:
+  bool _block;
+  std::queue<T> _queue;
+  std::condition_variable _condition;
+};
diff --git a/libfuse/util/fusermount.c b/libfuse/util/fusermount.c
index 512e80d..1451dda 100644
--- a/libfuse/util/fusermount.c
+++ b/libfuse/util/fusermount.c
@@ -726,7 +726,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
 	char *subtype = NULL;
 	char *source = NULL;
 	char *type = NULL;
-	int check_empty = 1;
 	int blkdev = 0;
 
 	optbuf = (char *) malloc(strlen(opts) + 128);
@@ -759,8 +758,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
 				goto err;
 			}
 			blkdev = 1;
-		} else if (opt_eq(s, len, "nonempty")) {
-			check_empty = 0;
 		} else if (opt_eq(s, len, "auto_unmount")) {
 			auto_unmount = 1;
 		} else if (!begins_with(s, "fd=") &&
@@ -811,10 +808,6 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
 	sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
 		fd, rootmode, getuid(), getgid());
 
-	if (check_empty &&
-	    fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1)
-		goto err;
-
 	source = malloc((fsname ? strlen(fsname) : 0) +
 			(subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
 
diff --git a/libfuse/util/mount_util.c b/libfuse/util/mount_util.c
index defb92f..6f094ec 100644
--- a/libfuse/util/mount_util.c
+++ b/libfuse/util/mount_util.c
@@ -312,39 +312,6 @@ char *fuse_mnt_resolve_path(const char *progname, const char *orig)
 	return dst;
 }
 
-int fuse_mnt_check_empty(const char *progname, const char *mnt,
-			 mode_t rootmode, off_t rootsize)
-{
-	int isempty = 1;
-
-	if (S_ISDIR(rootmode)) {
-		struct dirent *ent;
-		DIR *dp = opendir(mnt);
-		if (dp == NULL) {
-			fprintf(stderr,
-				"%s: failed to open mountpoint for reading: %s\n",
-				progname, strerror(errno));
-			return -1;
-		}
-		while ((ent = readdir(dp)) != NULL) {
-			if (strcmp(ent->d_name, ".") != 0 &&
-			    strcmp(ent->d_name, "..") != 0) {
-				isempty = 0;
-				break;
-			}
-		}
-		closedir(dp);
-	} else if (rootsize)
-		isempty = 0;
-
-	if (!isempty) {
-		fprintf(stderr, "%s: mountpoint is not empty\n", progname);
-		fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
-		return -1;
-	}
-	return 0;
-}
-
 int fuse_mnt_check_fuseblk(void)
 {
 	char buf[256];
diff --git a/man/mergerfs.1 b/man/mergerfs.1
index 505bcbc..9060aad 100644
--- a/man/mergerfs.1
+++ b/man/mergerfs.1
@@ -1,7 +1,7 @@
 .\"t
-.\" Automatically generated by Pandoc 1.19.2.4
+.\" Automatically generated by Pandoc 2.5
 .\"
-.TH "mergerfs" "1" "2021\-05\-29" "mergerfs user manual" ""
+.TH "mergerfs" "1" "2022\-05\-23" "mergerfs user manual" ""
 .hy
 .SH NAME
 .PP
@@ -11,10 +11,11 @@ mergerfs \- a featureful union filesystem
 mergerfs \-o<options> <branches> <mountpoint>
 .SH DESCRIPTION
 .PP
-\f[B]mergerfs\f[] is a union filesystem geared towards simplifying
+\f[B]mergerfs\f[R] is a union filesystem geared towards simplifying
 storage and management of files across numerous commodity storage
 devices.
-It is similar to \f[B]mhddfs\f[], \f[B]unionfs\f[], and \f[B]aufs\f[].
+It is similar to \f[B]mhddfs\f[R], \f[B]unionfs\f[R], and
+\f[B]aufs\f[R].
 .SH FEATURES
 .IP \[bu] 2
 Configurable behaviors / file placement
@@ -52,33 +53,33 @@ Read more about policies below.
 .IP
 .nf
 \f[C]
-A\ \ \ \ \ \ \ \ \ +\ \ \ \ \ \ B\ \ \ \ \ \ \ \ =\ \ \ \ \ \ \ C
-/disk1\ \ \ \ \ \ \ \ \ \ \ /disk2\ \ \ \ \ \ \ \ \ \ \ /merged
-|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |
-+\-\-\ /dir1\ \ \ \ \ \ \ \ +\-\-\ /dir1\ \ \ \ \ \ \ \ +\-\-\ /dir1
-|\ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ |
-|\ \ \ +\-\-\ file1\ \ \ \ |\ \ \ +\-\-\ file2\ \ \ \ |\ \ \ +\-\-\ file1
-|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file3\ \ \ \ |\ \ \ +\-\-\ file2
-+\-\-\ /dir2\ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file3
-|\ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ /dir3\ \ \ \ \ \ \ \ |
-|\ \ \ +\-\-\ file4\ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ /dir2
-|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ file5\ \ \ |\ \ \ |
-+\-\-\ file6\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file4
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ /dir3
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ |
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file5
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ file6
-\f[]
+A         +      B        =       C
+/disk1           /disk2           /merged
+|                |                |
++\-\- /dir1        +\-\- /dir1        +\-\- /dir1
+|   |            |   |            |   |
+|   +\-\- file1    |   +\-\- file2    |   +\-\- file1
+|                |   +\-\- file3    |   +\-\- file2
++\-\- /dir2        |                |   +\-\- file3
+|   |            +\-\- /dir3        |
+|   +\-\- file4        |            +\-\- /dir2
+|                     +\-\- file5   |   |
++\-\- file6                         |   +\-\- file4
+                                  |
+                                  +\-\- /dir3
+                                  |   |
+                                  |   +\-\- file5
+                                  |
+                                  +\-\- file6
+\f[R]
 .fi
 .PP
-mergerfs does \f[B]NOT\f[] support the copy\-on\-write (CoW) or whiteout
-behaviors found in \f[B]aufs\f[] and \f[B]overlayfs\f[].
-You can \f[B]not\f[] mount a read\-only filesystem and write to it.
+mergerfs does \f[B]NOT\f[R] support the copy\-on\-write (CoW) or
+whiteout behaviors found in \f[B]aufs\f[R] and \f[B]overlayfs\f[R].
+You can \f[B]not\f[R] mount a read\-only filesystem and write to it.
 However, mergerfs will ignore read\-only drives when creating new files
 so you can mix read\-write and read\-only drives.
-It also does \f[B]NOT\f[] split data across drives.
+It also does \f[B]NOT\f[R] split data across drives.
 It is not RAID0 / striping.
 It is simply a union of other filesystems.
 .SH TERMINOLOGY
@@ -101,15 +102,14 @@ path preservation: Aspect of some policies which includes checking the
 path for which a file would be created.
 .SH BASIC SETUP
 .PP
-If you don\[aq]t already know that you have a special use case then just
+If you don\[cq]t already know that you have a special use case then just
 start with one of the following option sets.
-.SS You need \f[C]mmap\f[] (used by rtorrent and many sqlite3 base
-software)
+.SS You need \f[C]mmap\f[R] (used by rtorrent and many sqlite3 base software)
 .PP
-\f[C]allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs\f[]
-.SS You don\[aq]t need \f[C]mmap\f[]
+\f[C]allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs\f[R]
+.SS You don\[cq]t need \f[C]mmap\f[R]
 .PP
-\f[C]allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs\f[]
+\f[C]allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs\f[R]
 .PP
 See the mergerfs wiki for real world
 deployments (https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments)
@@ -117,139 +117,137 @@ for comparisons / ideas.
 .SH OPTIONS
 .PP
 These options are the same regardless you use them with the
-\f[C]mergerfs\f[] commandline program, used in fstab, or in a config
+\f[C]mergerfs\f[R] commandline program, used in fstab, or in a config
 file.
 .SS mount options
 .IP \[bu] 2
-\f[B]config\f[]: Path to a config file.
+\f[B]config\f[R]: Path to a config file.
 Same arguments as below in key=val / ini style format.
 .IP \[bu] 2
-\f[B]branches\f[]: Colon delimited list of branches.
+\f[B]branches\f[R]: Colon delimited list of branches.
 .IP \[bu] 2
-\f[B]allow_other\f[]: A libfuse option which allows users besides the
+\f[B]allow_other\f[R]: A libfuse option which allows users besides the
 one which ran mergerfs to see the filesystem.
 This is required for most use\-cases.
 .IP \[bu] 2
-\f[B]minfreespace=SIZE\f[]: The minimum space value used for creation
+\f[B]minfreespace=SIZE\f[R]: The minimum space value used for creation
 policies.
 Can be overridden by branch specific option.
-Understands \[aq]K\[aq], \[aq]M\[aq], and \[aq]G\[aq] to represent
-kilobyte, megabyte, and gigabyte respectively.
+Understands `K', `M', and `G' to represent kilobyte, megabyte, and
+gigabyte respectively.
 (default: 4G)
 .IP \[bu] 2
-\f[B]moveonenospc=BOOL|POLICY\f[]: When enabled if a \f[B]write\f[]
-fails with \f[B]ENOSPC\f[] (no space left on device) or \f[B]EDQUOT\f[]
-(disk quota exceeded) the policy selected will run to find a new
-location for the file.
+\f[B]moveonenospc=BOOL|POLICY\f[R]: When enabled if a \f[B]write\f[R]
+fails with \f[B]ENOSPC\f[R] (no space left on device) or
+\f[B]EDQUOT\f[R] (disk quota exceeded) the policy selected will run to
+find a new location for the file.
 An attempt to move the file to that branch will occur (keeping all
 metadata possible) and if successful the original is unlinked and the
 write retried.
 (default: false, true = mfs)
 .IP \[bu] 2
-\f[B]use_ino\f[]: Causes mergerfs to supply file/directory inodes rather
-than libfuse.
+\f[B]use_ino\f[R]: Causes mergerfs to supply file/directory inodes
+rather than libfuse.
 While not a default it is recommended it be enabled so that linked files
 share the same inode value.
 .IP \[bu] 2
-\f[B]inodecalc=passthrough|path\-hash|devino\-hash|hybrid\-hash\f[]:
+\f[B]inodecalc=passthrough|path\-hash|devino\-hash|hybrid\-hash\f[R]:
 Selects the inode calculation algorithm.
 (default: hybrid\-hash)
 .IP \[bu] 2
-\f[B]dropcacheonclose=BOOL\f[]: When a file is requested to be closed
-call \f[C]posix_fadvise\f[] on it first to instruct the kernel that we
+\f[B]dropcacheonclose=BOOL\f[R]: When a file is requested to be closed
+call \f[C]posix_fadvise\f[R] on it first to instruct the kernel that we
 no longer need the data and it can drop its cache.
-Recommended when \f[B]cache.files=partial|full|auto\-full\f[] to limit
+Recommended when \f[B]cache.files=partial|full|auto\-full\f[R] to limit
 double caching.
 (default: false)
 .IP \[bu] 2
-\f[B]symlinkify=BOOL\f[]: When enabled and a file is not writable and
-its mtime or ctime is older than \f[B]symlinkify_timeout\f[] files will
+\f[B]symlinkify=BOOL\f[R]: When enabled and a file is not writable and
+its mtime or ctime is older than \f[B]symlinkify_timeout\f[R] files will
 be reported as symlinks to the original files.
 Please read more below before using.
 (default: false)
 .IP \[bu] 2
-\f[B]symlinkify_timeout=UINT\f[]: Time to wait, in seconds, to activate
-the \f[B]symlinkify\f[] behavior.
+\f[B]symlinkify_timeout=UINT\f[R]: Time to wait, in seconds, to activate
+the \f[B]symlinkify\f[R] behavior.
 (default: 3600)
 .IP \[bu] 2
-\f[B]nullrw=BOOL\f[]: Turns reads and writes into no\-ops.
+\f[B]nullrw=BOOL\f[R]: Turns reads and writes into no\-ops.
 The request will succeed but do nothing.
 Useful for benchmarking mergerfs.
 (default: false)
 .IP \[bu] 2
-\f[B]ignorepponrename=BOOL\f[]: Ignore path preserving on rename.
+\f[B]ignorepponrename=BOOL\f[R]: Ignore path preserving on rename.
 Typically rename and link act differently depending on the policy of
-\f[C]create\f[] (read below).
+\f[C]create\f[R] (read below).
 Enabling this will cause rename and link to always use the non\-path
 preserving behavior.
 This means files, when renamed or linked, will stay on the same drive.
 (default: false)
 .IP \[bu] 2
-\f[B]security_capability=BOOL\f[]: If false return ENOATTR when xattr
+\f[B]security_capability=BOOL\f[R]: If false return ENOATTR when xattr
 security.capability is queried.
 (default: true)
 .IP \[bu] 2
-\f[B]xattr=passthrough|noattr|nosys\f[]: Runtime control of xattrs.
+\f[B]xattr=passthrough|noattr|nosys\f[R]: Runtime control of xattrs.
 Default is to passthrough xattr requests.
-\[aq]noattr\[aq] will short circuit as if nothing exists.
-\[aq]nosys\[aq] will respond with ENOSYS as if xattrs are not supported
-or disabled.
+`noattr' will short circuit as if nothing exists.
+`nosys' will respond with ENOSYS as if xattrs are not supported or
+disabled.
 (default: passthrough)
 .IP \[bu] 2
-\f[B]link_cow=BOOL\f[]: When enabled if a regular file is opened which
+\f[B]link_cow=BOOL\f[R]: When enabled if a regular file is opened which
 has a link count > 1 it will copy the file to a temporary file and
 rename over the original.
 Breaking the link and providing a basic copy\-on\-write function similar
 to cow\-shell.
 (default: false)
 .IP \[bu] 2
-\f[B]statfs=base|full\f[]: Controls how statfs works.
-\[aq]base\[aq] means it will always use all branches in statfs
-calculations.
-\[aq]full\[aq] is in effect path preserving and only includes drives
-where the path exists.
+\f[B]statfs=base|full\f[R]: Controls how statfs works.
+`base' means it will always use all branches in statfs calculations.
+`full' is in effect path preserving and only includes drives where the
+path exists.
 (default: base)
 .IP \[bu] 2
-\f[B]statfs_ignore=none|ro|nc\f[]: \[aq]ro\[aq] will cause statfs
-calculations to ignore available space for branches mounted or tagged as
-\[aq]read\-only\[aq] or \[aq]no create\[aq].
-\[aq]nc\[aq] will ignore available space for branches tagged as \[aq]no
-create\[aq].
+\f[B]statfs_ignore=none|ro|nc\f[R]: `ro' will cause statfs calculations
+to ignore available space for branches mounted or tagged as `read\-only'
+or `no create'.
+`nc' will ignore available space for branches tagged as `no create'.
 (default: none)
 .IP \[bu] 2
-\f[B]nfsopenhack=off|git|all\f[]: A workaround for exporting mergerfs
+\f[B]nfsopenhack=off|git|all\f[R]: A workaround for exporting mergerfs
 over NFS where there are issues with creating files for write while
 setting the mode to read\-only.
 (default: off)
 .IP \[bu] 2
-\f[B]follow\-symlinks=never|directory|regular|all\f[]: Turns symlinks
+\f[B]follow\-symlinks=never|directory|regular|all\f[R]: Turns symlinks
 into what they point to.
 (default: never)
 .IP \[bu] 2
-\f[B]link\-exdev=passthrough|rel\-symlink|abs\-base\-symlink|abs\-pool\-symlink\f[]:
+\f[B]link\-exdev=passthrough|rel\-symlink|abs\-base\-symlink|abs\-pool\-symlink\f[R]:
 When a link fails with EXDEV optionally create a symlink to the file
 instead.
 .IP \[bu] 2
-\f[B]rename\-exdev=passthrough|rel\-symlink|abs\-symlink\f[]: When a
+\f[B]rename\-exdev=passthrough|rel\-symlink|abs\-symlink\f[R]: When a
 rename fails with EXDEV optionally move the file to a special directory
 and symlink to it.
 .IP \[bu] 2
-\f[B]posix_acl=BOOL\f[]: Enable POSIX ACL support (if supported by
+\f[B]posix_acl=BOOL\f[R]: Enable POSIX ACL support (if supported by
 kernel and underlying filesystem).
 (default: false)
 .IP \[bu] 2
-\f[B]async_read=BOOL\f[]: Perform reads asynchronously.
+\f[B]async_read=BOOL\f[R]: Perform reads asynchronously.
 If disabled or unavailable the kernel will ensure there is at most one
 pending read request per file handle and will attempt to order requests
 by offset.
 (default: true)
 .IP \[bu] 2
-\f[B]fuse_msg_size=UINT\f[]: Set the max number of pages per FUSE
+\f[B]fuse_msg_size=UINT\f[R]: Set the max number of pages per FUSE
 message.
 Only available on Linux >= 4.20 and ignored otherwise.
 (min: 1; max: 256; default: 256)
 .IP \[bu] 2
-\f[B]threads=INT\f[]: Number of threads to use in multithreaded mode.
+\f[B]threads=INT\f[R]: Number of threads to use in multithreaded mode.
 When set to zero it will attempt to discover and use the number of
 logical cores.
 If the lookup fails it will fall back to using 4.
@@ -262,90 +260,89 @@ NOTE: higher number of threads increases parallelism but usually
 decreases throughput.
 (default: 0)
 .IP \[bu] 2
-\f[B]fsname=STR\f[]: Sets the name of the filesystem as seen in
-\f[B]mount\f[], \f[B]df\f[], etc.
+\f[B]fsname=STR\f[R]: Sets the name of the filesystem as seen in
+\f[B]mount\f[R], \f[B]df\f[R], etc.
 Defaults to a list of the source paths concatenated together with the
 longest common prefix removed.
 .IP \[bu] 2
-\f[B]func.FUNC=POLICY\f[]: Sets the specific FUSE function\[aq]s policy.
+\f[B]func.FUNC=POLICY\f[R]: Sets the specific FUSE function\[cq]s
+policy.
 See below for the list of value types.
-Example: \f[B]func.getattr=newest\f[]
+Example: \f[B]func.getattr=newest\f[R]
 .IP \[bu] 2
-\f[B]category.action=POLICY\f[]: Sets policy of all FUSE functions in
+\f[B]category.action=POLICY\f[R]: Sets policy of all FUSE functions in
 the action category.
 (default: epall)
 .IP \[bu] 2
-\f[B]category.create=POLICY\f[]: Sets policy of all FUSE functions in
+\f[B]category.create=POLICY\f[R]: Sets policy of all FUSE functions in
 the create category.
 (default: epmfs)
 .IP \[bu] 2
-\f[B]category.search=POLICY\f[]: Sets policy of all FUSE functions in
+\f[B]category.search=POLICY\f[R]: Sets policy of all FUSE functions in
 the search category.
 (default: ff)
 .IP \[bu] 2
-\f[B]cache.open=UINT\f[]: \[aq]open\[aq] policy cache timeout in
-seconds.
+\f[B]cache.open=UINT\f[R]: `open' policy cache timeout in seconds.
 (default: 0)
 .IP \[bu] 2
-\f[B]cache.statfs=UINT\f[]: \[aq]statfs\[aq] cache timeout in seconds.
+\f[B]cache.statfs=UINT\f[R]: `statfs' cache timeout in seconds.
 (default: 0)
 .IP \[bu] 2
-\f[B]cache.attr=UINT\f[]: File attribute cache timeout in seconds.
+\f[B]cache.attr=UINT\f[R]: File attribute cache timeout in seconds.
 (default: 1)
 .IP \[bu] 2
-\f[B]cache.entry=UINT\f[]: File name lookup cache timeout in seconds.
+\f[B]cache.entry=UINT\f[R]: File name lookup cache timeout in seconds.
 (default: 1)
 .IP \[bu] 2
-\f[B]cache.negative_entry=UINT\f[]: Negative file name lookup cache
+\f[B]cache.negative_entry=UINT\f[R]: Negative file name lookup cache
 timeout in seconds.
 (default: 0)
 .IP \[bu] 2
-\f[B]cache.files=libfuse|off|partial|full|auto\-full\f[]: File page
+\f[B]cache.files=libfuse|off|partial|full|auto\-full\f[R]: File page
 caching mode (default: libfuse)
 .IP \[bu] 2
-\f[B]cache.writeback=BOOL\f[]: Enable kernel writeback caching (default:
-false)
+\f[B]cache.writeback=BOOL\f[R]: Enable kernel writeback caching
+(default: false)
 .IP \[bu] 2
-\f[B]cache.symlinks=BOOL\f[]: Cache symlinks (if supported by kernel)
+\f[B]cache.symlinks=BOOL\f[R]: Cache symlinks (if supported by kernel)
 (default: false)
 .IP \[bu] 2
-\f[B]cache.readdir=BOOL\f[]: Cache readdir (if supported by kernel)
+\f[B]cache.readdir=BOOL\f[R]: Cache readdir (if supported by kernel)
 (default: false)
 .IP \[bu] 2
-\f[B]direct_io\f[]: deprecated \- Bypass page cache.
-Use \f[C]cache.files=off\f[] instead.
+\f[B]direct_io\f[R]: deprecated \- Bypass page cache.
+Use \f[C]cache.files=off\f[R] instead.
 (default: false)
 .IP \[bu] 2
-\f[B]kernel_cache\f[]: deprecated \- Do not invalidate data cache on
+\f[B]kernel_cache\f[R]: deprecated \- Do not invalidate data cache on
 file open.
-Use \f[C]cache.files=full\f[] instead.
+Use \f[C]cache.files=full\f[R] instead.
 (default: false)
 .IP \[bu] 2
-\f[B]auto_cache\f[]: deprecated \- Invalidate data cache if file mtime
+\f[B]auto_cache\f[R]: deprecated \- Invalidate data cache if file mtime
 or size change.
-Use \f[C]cache.files=auto\-full\f[] instead.
+Use \f[C]cache.files=auto\-full\f[R] instead.
 (default: false)
 .IP \[bu] 2
-\f[B]async_read\f[]: deprecated \- Perform reads asynchronously.
-Use \f[C]async_read=true\f[] instead.
+\f[B]async_read\f[R]: deprecated \- Perform reads asynchronously.
+Use \f[C]async_read=true\f[R] instead.
 .IP \[bu] 2
-\f[B]sync_read\f[]: deprecated \- Perform reads synchronously.
-Use \f[C]async_read=false\f[] instead.
+\f[B]sync_read\f[R]: deprecated \- Perform reads synchronously.
+Use \f[C]async_read=false\f[R] instead.
 .PP
-\f[B]NOTE:\f[] Options are evaluated in the order listed so if the
-options are \f[B]func.rmdir=rand,category.action=ff\f[] the
-\f[B]action\f[] category setting will override the \f[B]rmdir\f[]
+\f[B]NOTE:\f[R] Options are evaluated in the order listed so if the
+options are \f[B]func.rmdir=rand,category.action=ff\f[R] the
+\f[B]action\f[R] category setting will override the \f[B]rmdir\f[R]
 setting.
 .SS Value Types
 .IP \[bu] 2
-BOOL = \[aq]true\[aq] | \[aq]false\[aq]
+BOOL = `true' | `false'
 .IP \[bu] 2
 INT = [MIN_INT,MAX_INT]
 .IP \[bu] 2
 UINT = [0,MAX_INT]
 .IP \[bu] 2
-SIZE = \[aq]NNM\[aq]; NN = INT, M = \[aq]K\[aq] | \[aq]M\[aq] |
-\[aq]G\[aq] | \[aq]T\[aq]
+SIZE = `NNM'; NN = INT, M = `K' | `M' | `G' | `T'
 .IP \[bu] 2
 STR = string
 .IP \[bu] 2
@@ -356,19 +353,19 @@ CATEGORY = function category
 POLICY = mergerfs function policy
 .SS branches
 .PP
-The \[aq]branches\[aq] argument is a colon (\[aq]:\[aq]) delimited list
-of paths to be pooled together.
+The `branches' argument is a colon (`:') delimited list of paths to be
+pooled together.
 It does not matter if the paths are on the same or different drives nor
 does it matter the filesystem (within reason).
 Used and available space will not be duplicated for paths on the same
-device and any features which aren\[aq]t supported by the underlying
+device and any features which aren\[cq]t supported by the underlying
 filesystem (such as file attributes or extended attributes) will return
 the appropriate errors.
 .PP
 Branches currently have two options which can be set.
 A type which impacts whether or not the branch is included in a policy
 calculation and a individual minfreespace value.
-The values are set by prepending an \f[C]=\f[] at the end of a branch
+The values are set by prepending an \f[C]=\f[R] at the end of a branch
 designation and using commas as delimiters.
 Example: /mnt/drive=RW,1234
 .SS branch type
@@ -376,13 +373,13 @@ Example: /mnt/drive=RW,1234
 RW: (read/write) \- Default behavior.
 Will be eligible in all policy categories.
 .IP \[bu] 2
-RO: (read\-only) \- Will be excluded from \f[C]create\f[] and
-\f[C]action\f[] policies.
+RO: (read\-only) \- Will be excluded from \f[C]create\f[R] and
+\f[C]action\f[R] policies.
 Same as a read\-only mounted filesystem would be (though faster to
 process).
 .IP \[bu] 2
-NC: (no\-create) \- Will be excluded from \f[C]create\f[] policies.
-You can\[aq]t create on that branch but you can change or delete.
+NC: (no\-create) \- Will be excluded from \f[C]create\f[R] policies.
+You can\[cq]t create on that branch but you can change or delete.
 .SS minfreespace
 .PP
 Same purpose as the global option but specific to the branch.
@@ -392,35 +389,35 @@ If not set the global value is used.
 To make it easier to include multiple branches mergerfs supports
 globbing (http://linux.die.net/man/7/glob).
 \f[B]The globbing tokens MUST be escaped when using via the shell else
-the shell itself will apply the glob itself.\f[]
+the shell itself will apply the glob itself.\f[R]
 .IP
 .nf
 \f[C]
-#\ mergerfs\ \-o\ allow_other,use_ino\ /mnt/disk\\*:/mnt/cdrom\ /media/drives
-\f[]
+# mergerfs \-o allow_other,use_ino /mnt/disk\[rs]*:/mnt/cdrom /media/drives
+\f[R]
 .fi
 .PP
 The above line will use all mount points in /mnt prefixed with
-\f[B]disk\f[] and the \f[B]cdrom\f[].
+\f[B]disk\f[R] and the \f[B]cdrom\f[R].
 .PP
 To have the pool mounted at boot or otherwise accessible from related
-tools use \f[B]/etc/fstab\f[].
+tools use \f[B]/etc/fstab\f[R].
 .IP
 .nf
 \f[C]
-#\ <file\ system>\ \ \ \ \ \ \ \ <mount\ point>\ \ <type>\ \ \ \ \ \ \ \ \ <options>\ \ \ \ \ \ \ \ \ \ \ \ \ <dump>\ \ <pass>
-/mnt/disk*:/mnt/cdrom\ \ /mnt/pool\ \ \ \ \ \ fuse.mergerfs\ \ allow_other,use_ino\ \ \ 0\ \ \ \ \ \ \ 0
-\f[]
+# <file system>        <mount point>  <type>         <options>             <dump>  <pass>
+/mnt/disk*:/mnt/cdrom  /mnt/pool      fuse.mergerfs  allow_other,use_ino   0       0
+\f[R]
 .fi
 .PP
-\f[B]NOTE:\f[] the globbing is done at mount or when updated using the
+\f[B]NOTE:\f[R] the globbing is done at mount or when updated using the
 runtime API.
 If a new directory is added matching the glob after the fact it will not
 be automatically included.
 .PP
-\f[B]NOTE:\f[] for mounting via \f[B]fstab\f[] to work you must have
-\f[B]mount.fuse\f[] installed.
-For Ubuntu/Debian it is included in the \f[B]fuse\f[] package.
+\f[B]NOTE:\f[R] for mounting via \f[B]fstab\f[R] to work you must have
+\f[B]mount.fuse\f[R] installed.
+For Ubuntu/Debian it is included in the \f[B]fuse\f[R] package.
 .SS inodecalc
 .PP
 Inodes (st_ino) are unique identifiers within a filesystem.
@@ -434,14 +431,14 @@ the complexity they add.
 .PP
 FUSE allows the server (mergerfs) to set inode values but not device
 IDs.
-Creating an inode value is somewhat complex in mergerfs\[aq] case as
-files aren\[aq]t really in its control.
+Creating an inode value is somewhat complex in mergerfs\[cq] case as
+files aren\[cq]t really in its control.
 If a policy changes what directory or file is to be selected or
 something changes out of band it becomes unclear what value should be
 used.
 Most software does not to care what the values are but those that do
 often break if a value changes unexpectedly.
-The tool \f[C]find\f[] will abort a directory walk if it sees a
+The tool \f[C]find\f[R] will abort a directory walk if it sees a
 directory inode change.
 NFS will return stale handle errors if the inode changes out of band.
 File dedup tools will usually leverage device ids and inodes as a
@@ -457,27 +454,27 @@ problems mentioned above and could confuse file deduplication software
 as inodes from different filesystems can be the same.
 .IP \[bu] 2
 path\-hash: Hashes the relative path of the entry in question.
-The underlying file\[aq]s values are completely ignored.
+The underlying file\[cq]s values are completely ignored.
 This means the inode value will always be the same for that file path.
 This is useful when using NFS and you make changes out of band such as
 copy data between branches.
 This also means that entries that do point to the same file will not be
 recognizable via inodes.
-That \f[B]does not\f[] mean hard links don\[aq]t work.
+That \f[B]does not\f[R] mean hard links don\[cq]t work.
 They will.
 .IP \[bu] 2
 path\-hash32: 32bit version of path\-hash.
 .IP \[bu] 2
 devino\-hash: Hashes the device id and inode of the underlying entry.
-This won\[aq]t prevent issues with NFS should the policy pick a
+This won\[cq]t prevent issues with NFS should the policy pick a
 different file or files move out of band but will present the same inode
 for underlying files that do too.
 .IP \[bu] 2
 devino\-hash32: 32bit version of devino\-hash.
 .IP \[bu] 2
-hybrid\-hash: Performs \f[C]path\-hash\f[] on directories and
-\f[C]devino\-hash\f[] on other file types.
-Since directories can\[aq]t have hard links the static value won\[aq]t
+hybrid\-hash: Performs \f[C]path\-hash\f[R] on directories and
+\f[C]devino\-hash\f[R] on other file types.
+Since directories can\[cq]t have hard links the static value won\[cq]t
 make a difference and the files will get values useful for finding
 duplicates.
 Probably the best to use if not using NFS.
@@ -494,36 +491,36 @@ Unlike a typical filesystem FUSE filesystems can reuse inodes and not
 refer to the same entry.
 The internal identifier used to reference a file in FUSE is different
 from the inode value presented.
-The former is the \f[C]nodeid\f[] and is actually a tuple of 2 64bit
-values: \f[C]nodeid\f[] and \f[C]generation\f[].
+The former is the \f[C]nodeid\f[R] and is actually a tuple of 2 64bit
+values: \f[C]nodeid\f[R] and \f[C]generation\f[R].
 This tuple is not client facing.
 The inode that is presented to the client is passed through the kernel
 uninterpreted.
 .PP
-From FUSE docs regarding \f[C]use_ino\f[]:
+From FUSE docs regarding \f[C]use_ino\f[R]:
 .IP
 .nf
 \f[C]
-Honor\ the\ st_ino\ field\ in\ the\ functions\ getattr()\ and
-fill_dir().\ This\ value\ is\ used\ to\ fill\ in\ the\ st_ino\ field
-in\ the\ stat(2),\ lstat(2),\ fstat(2)\ functions\ and\ the\ d_ino
-field\ in\ the\ readdir(2)\ function.\ The\ filesystem\ does\ not
-have\ to\ guarantee\ uniqueness,\ however\ some\ applications
-rely\ on\ this\ value\ being\ unique\ for\ the\ whole\ filesystem.
-Note\ that\ this\ does\ *not*\ affect\ the\ inode\ that\ libfuse
-and\ the\ kernel\ use\ internally\ (also\ called\ the\ "nodeid").
-\f[]
+Honor the st_ino field in the functions getattr() and
+fill_dir(). This value is used to fill in the st_ino field
+in the stat(2), lstat(2), fstat(2) functions and the d_ino
+field in the readdir(2) function. The filesystem does not
+have to guarantee uniqueness, however some applications
+rely on this value being unique for the whole filesystem.
+Note that this does *not* affect the inode that libfuse
+and the kernel use internally (also called the \[dq]nodeid\[dq]).
+\f[R]
 .fi
 .PP
-In the future the \f[C]use_ino\f[] option will probably be removed as
+In the future the \f[C]use_ino\f[R] option will probably be removed as
 this feature should replace the original libfuse inode calculation
 strategy.
-Currently you still need to use \f[C]use_ino\f[] in order to enable
-\f[C]inodecalc\f[].
+Currently you still need to use \f[C]use_ino\f[R] in order to enable
+\f[C]inodecalc\f[R].
 .SS fuse_msg_size
 .PP
 FUSE applications communicate with the kernel over a special character
-device: \f[C]/dev/fuse\f[].
+device: \f[C]/dev/fuse\f[R].
 A large portion of the overhead associated with FUSE is the cost of
 going back and forth from user space and kernel space over that device.
 Generally speaking the fewer trips needed the better the performance
@@ -538,16 +535,16 @@ In Linux 4.20 a new feature was added allowing the negotiation of the
 max message size.
 Since the size is in multiples of
 pages (https://en.wikipedia.org/wiki/Page_(computer_memory)) the feature
-is called \f[C]max_pages\f[].
-There is a maximum \f[C]max_pages\f[] value of 256 (1MiB) and minimum of
-1 (4KiB).
+is called \f[C]max_pages\f[R].
+There is a maximum \f[C]max_pages\f[R] value of 256 (1MiB) and minimum
+of 1 (4KiB).
 The default used by Linux >=4.20, and hardcoded value used before 4.20,
 is 32 (128KiB).
-In mergerfs its referred to as \f[C]fuse_msg_size\f[] to make it clear
+In mergerfs its referred to as \f[C]fuse_msg_size\f[R] to make it clear
 what it impacts and provide some abstraction.
 .PP
-Since there should be no downsides to increasing \f[C]fuse_msg_size\f[]
-/ \f[C]max_pages\f[], outside a minor bump in RAM usage due to larger
+Since there should be no downsides to increasing \f[C]fuse_msg_size\f[R]
+/ \f[C]max_pages\f[R], outside a minor bump in RAM usage due to larger
 message buffers, mergerfs defaults the value to 256.
 On kernels before 4.20 the value has no effect.
 The reason the value is configurable is to enable experimentation and
@@ -559,11 +556,11 @@ This feature, when enabled, will cause symlinks to be interpreted by
 mergerfs as their target (depending on the mode).
 .PP
 When there is a getattr/stat request for a file mergerfs will check if
-the file is a symlink and depending on the \f[C]follow\-symlinks\f[]
+the file is a symlink and depending on the \f[C]follow\-symlinks\f[R]
 setting will replace the information about the symlink with that of that
 which it points to.
 .PP
-When unlink\[aq]ing or rmdir\[aq]ing the followed symlink it will remove
+When unlink\[cq]ing or rmdir\[cq]ing the followed symlink it will remove
 the symlink itself and not that which it points to.
 .IP \[bu] 2
 never: Behave as normal.
@@ -582,15 +579,15 @@ If you find any odd behaviors please file a ticket on
 github (https://github.com/trapexit/mergerfs/issues).
 .SS link\-exdev
 .PP
-If using path preservation and a \f[C]link\f[] fails with EXDEV make a
-call to \f[C]symlink\f[] where the \f[C]target\f[] is the
-\f[C]oldlink\f[] and the \f[C]linkpath\f[] is the \f[C]newpath\f[].
-The \f[C]target\f[] value is determined by the value of
-\f[C]link\-exdev\f[].
+If using path preservation and a \f[C]link\f[R] fails with EXDEV make a
+call to \f[C]symlink\f[R] where the \f[C]target\f[R] is the
+\f[C]oldlink\f[R] and the \f[C]linkpath\f[R] is the \f[C]newpath\f[R].
+The \f[C]target\f[R] value is determined by the value of
+\f[C]link\-exdev\f[R].
 .IP \[bu] 2
 passthrough: Return EXDEV as normal.
 .IP \[bu] 2
-rel\-symlink: A relative path from the \f[C]newpath\f[].
+rel\-symlink: A relative path from the \f[C]newpath\f[R].
 .IP \[bu] 2
 abs\-base\-symlink: A absolute value using the underlying branch.
 .IP \[bu] 2
@@ -600,44 +597,44 @@ NOTE: It is possible that some applications check the file they link.
 In those cases it is possible it will error or complain.
 .SS rename\-exdev
 .PP
-If using path preservation and a \f[C]rename\f[] fails with EXDEV:
+If using path preservation and a \f[C]rename\f[R] fails with EXDEV:
 .IP "1." 3
-Move file from \f[B]/branch/a/b/c\f[] to
-\f[B]/branch/.mergerfs_rename_exdev/a/b/c\f[].
+Move file from \f[B]/branch/a/b/c\f[R] to
+\f[B]/branch/.mergerfs_rename_exdev/a/b/c\f[R].
 .IP "2." 3
-symlink the rename\[aq]s \f[C]newpath\f[] to the moved file.
+symlink the rename\[cq]s \f[C]newpath\f[R] to the moved file.
 .PP
-The \f[C]target\f[] value is determined by the value of
-\f[C]rename\-exdev\f[].
+The \f[C]target\f[R] value is determined by the value of
+\f[C]rename\-exdev\f[R].
 .IP \[bu] 2
 passthrough: Return EXDEV as normal.
 .IP \[bu] 2
-rel\-symlink: A relative path from the \f[C]newpath\f[].
+rel\-symlink: A relative path from the \f[C]newpath\f[R].
 .IP \[bu] 2
 abs\-symlink: A absolute value using the mergerfs mount point.
 .PP
 NOTE: It is possible that some applications check the file they rename.
 In those cases it is possible it will error or complain.
 .PP
-NOTE: The reason \f[C]abs\-symlink\f[] is not split into two like
-\f[C]link\-exdev\f[] is due to the complexities in managing absolute
-base symlinks when multiple \f[C]oldpaths\f[] exist.
+NOTE: The reason \f[C]abs\-symlink\f[R] is not split into two like
+\f[C]link\-exdev\f[R] is due to the complexities in managing absolute
+base symlinks when multiple \f[C]oldpaths\f[R] exist.
 .SS symlinkify
 .PP
 Due to the levels of indirection introduced by mergerfs and the
 underlying technology FUSE there can be varying levels of performance
 degradation.
 This feature will turn non\-directories which are not writable into
-symlinks to the original file found by the \f[C]readlink\f[] policy
+symlinks to the original file found by the \f[C]readlink\f[R] policy
 after the mtime and ctime are older than the timeout.
 .PP
-\f[B]WARNING:\f[] The current implementation has a known issue in which
+\f[B]WARNING:\f[R] The current implementation has a known issue in which
 if the file is open and being used when the file is converted to a
 symlink then the application which has that file open will receive an
 error when using it.
 This is unlikely to occur in practice but is something to keep in mind.
 .PP
-\f[B]WARNING:\f[] Some backup solutions, such as CrashPlan, do not
+\f[B]WARNING:\f[R] Some backup solutions, such as CrashPlan, do not
 backup the target of a symlink.
 If using this feature it will be necessary to point any backup software
 to the original drives or configure the software to follow symlinks if
@@ -647,46 +644,46 @@ One for backup and one for general consumption.
 .SS nullrw
 .PP
 Due to how FUSE works there is an overhead to all requests made to a
-FUSE filesystem that wouldn\[aq]t exist for an in kernel one.
+FUSE filesystem that wouldn\[cq]t exist for an in kernel one.
 Meaning that even a simple passthrough will have some slowdown.
 However, generally the overhead is minimal in comparison to the cost of
 the underlying I/O.
 By disabling the underlying I/O we can test the theoretical performance
 boundaries.
 .PP
-By enabling \f[C]nullrw\f[] mergerfs will work as it always does
-\f[B]except\f[] that all reads and writes will be no\-ops.
+By enabling \f[C]nullrw\f[R] mergerfs will work as it always does
+\f[B]except\f[R] that all reads and writes will be no\-ops.
 A write will succeed (the size of the write will be returned as if it
 were successful) but mergerfs does nothing with the data it was given.
-Similarly a read will return the size requested but won\[aq]t touch the
+Similarly a read will return the size requested but won\[cq]t touch the
 buffer.
 .PP
 See the BENCHMARKING section for suggestions on how to test.
 .SS xattr
 .PP
-Runtime extended attribute support can be managed via the \f[C]xattr\f[]
-option.
+Runtime extended attribute support can be managed via the
+\f[C]xattr\f[R] option.
 By default it will passthrough any xattr calls.
 Given xattr support is rarely used and can have significant performance
 implications mergerfs allows it to be disabled at runtime.
 The performance problems mostly comes when file caching is enabled.
-The kernel will send a \f[C]getxattr\f[] for
-\f[C]security.capability\f[] \f[I]before every single write\f[].
-It doesn\[aq]t cache the responses to any \f[C]getxattr\f[].
+The kernel will send a \f[C]getxattr\f[R] for
+\f[C]security.capability\f[R] \f[I]before every single write\f[R].
+It doesn\[cq]t cache the responses to any \f[C]getxattr\f[R].
 This might be addressed in the future but for now mergerfs can really
 only offer the following workarounds.
 .PP
-\f[C]noattr\f[] will cause mergerfs to short circuit all xattr calls and
-return ENOATTR where appropriate.
+\f[C]noattr\f[R] will cause mergerfs to short circuit all xattr calls
+and return ENOATTR where appropriate.
 mergerfs still gets all the requests but they will not be forwarded on
 to the underlying filesystems.
 The runtime control will still function in this mode.
 .PP
-\f[C]nosys\f[] will cause mergerfs to return ENOSYS for any xattr call.
-The difference with \f[C]noattr\f[] is that the kernel will cache this
+\f[C]nosys\f[R] will cause mergerfs to return ENOSYS for any xattr call.
+The difference with \f[C]noattr\f[R] is that the kernel will cache this
 fact and itself short circuit future calls.
-This is more efficient than \f[C]noattr\f[] but will cause mergerfs\[aq]
-runtime control via the hidden file to stop working.
+This is more efficient than \f[C]noattr\f[R] but will cause
+mergerfs\[cq] runtime control via the hidden file to stop working.
 .SS nfsopenhack
 .PP
 NFS is not fully POSIX compliant and historically certain behaviors,
@@ -703,53 +700,53 @@ of the NFS server and clients but it results in a permission error
 because a normal user is not allowed to open a read\-only file as
 writable.
 .PP
-Even though it\[aq]s a more niche situation this hack breaks normal
-security and behavior and as such is \f[C]off\f[] by default.
-If set to \f[C]git\f[] it will only perform the hack when the path in
-question includes \f[C]/.git/\f[].
-\f[C]all\f[] will result it it applying anytime a readonly file which is
+Even though it\[cq]s a more niche situation this hack breaks normal
+security and behavior and as such is \f[C]off\f[R] by default.
+If set to \f[C]git\f[R] it will only perform the hack when the path in
+question includes \f[C]/.git/\f[R].
+\f[C]all\f[R] will result it applying anytime a readonly file which is
 empty is opened for writing.
 .SH FUNCTIONS, CATEGORIES and POLICIES
 .PP
 The POSIX filesystem API is made up of a number of functions.
-\f[B]creat\f[], \f[B]stat\f[], \f[B]chown\f[], etc.
+\f[B]creat\f[R], \f[B]stat\f[R], \f[B]chown\f[R], etc.
 For ease of configuration in mergerfs most of the core functions are
-grouped into 3 categories: \f[B]action\f[], \f[B]create\f[], and
-\f[B]search\f[].
+grouped into 3 categories: \f[B]action\f[R], \f[B]create\f[R], and
+\f[B]search\f[R].
 These functions and categories can be assigned a policy which dictates
 which branch is chosen when performing that function.
 .PP
-Some functions, listed in the category \f[C]N/A\f[] below, can not be
+Some functions, listed in the category \f[C]N/A\f[R] below, can not be
 assigned the normal policies.
 These functions work with file handles, rather than file paths, which
-were created by \f[C]open\f[] or \f[C]create\f[].
+were created by \f[C]open\f[R] or \f[C]create\f[R].
 That said many times the current FUSE kernel driver will not always
-provide the file handle when a client calls \f[C]fgetattr\f[],
-\f[C]fchown\f[], \f[C]fchmod\f[], \f[C]futimens\f[], \f[C]ftruncate\f[],
-etc.
+provide the file handle when a client calls \f[C]fgetattr\f[R],
+\f[C]fchown\f[R], \f[C]fchmod\f[R], \f[C]futimens\f[R],
+\f[C]ftruncate\f[R], etc.
 This means it will call the regular, path based, versions.
-\f[C]readdir\f[] has no real need for a policy given the purpose is
+\f[C]readdir\f[R] has no real need for a policy given the purpose is
 merely to return a list of entries in a directory.
-\f[C]statfs\f[]\[aq]s behavior can be modified via other options.
+\f[C]statfs\f[R]\[cq]s behavior can be modified via other options.
 .PP
-When using policies which are based on a branch\[aq]s available space
+When using policies which are based on a branch\[cq]s available space
 the base path provided is used.
 Not the full path to the file in question.
-Meaning that mounts in the branch won\[aq]t be considered in the space
+Meaning that mounts in the branch won\[cq]t be considered in the space
 calculations.
-The reason is that it doesn\[aq]t really work for non\-path preserving
+The reason is that it doesn\[cq]t really work for non\-path preserving
 policies and can lead to non\-obvious behaviors.
 .PP
 NOTE: While any policy can be assigned to a function or category though
 some may not be very useful in practice.
-For instance: \f[B]rand\f[] (random) may be useful for file creation
-(create) but could lead to very odd behavior if used for \f[C]chmod\f[]
+For instance: \f[B]rand\f[R] (random) may be useful for file creation
+(create) but could lead to very odd behavior if used for \f[C]chmod\f[R]
 if there were more than one copy of the file.
 .SS Functions and their Category classifications
 .PP
 .TS
 tab(@);
-lw(7.9n) lw(62.1n).
+lw(7.4n) lw(62.6n).
 T{
 Category
 T}@T{
@@ -782,52 +779,54 @@ T}
 .TE
 .PP
 In cases where something may be searched for (such as a path to clone)
-\f[B]getattr\f[] will usually be used.
+\f[B]getattr\f[R] will usually be used.
 .SS Policies
 .PP
 A policy is the algorithm used to choose a branch or branches for a
 function to work on.
 Think of them as ways to filter and sort branches.
 .PP
-Any function in the \f[C]create\f[] category will clone the relative
+Any function in the \f[C]create\f[R] category will clone the relative
 path if needed.
-Some other functions (\f[C]rename\f[],\f[C]link\f[],\f[C]ioctl\f[]) have
-special requirements or behaviors which you can read more about below.
+Some other functions (\f[C]rename\f[R],\f[C]link\f[R],\f[C]ioctl\f[R])
+have special requirements or behaviors which you can read more about
+below.
 .SS Filtering
 .PP
 Policies basically search branches and create a list of files / paths
 for functions to work on.
 The policy is responsible for filtering and sorting the branches.
-Filters include \f[B]minfreespace\f[], whether or not a branch is
+Filters include \f[B]minfreespace\f[R], whether or not a branch is
 mounted read\-only, and the branch tagging (RO,NC,RW).
 These filters are applied across all policies unless otherwise noted.
 .IP \[bu] 2
-No \f[B]search\f[] function policies filter.
+No \f[B]search\f[R] function policies filter.
 .IP \[bu] 2
-All \f[B]action\f[] function policies filter out branches which are
-mounted \f[B]read\-only\f[] or tagged as \f[B]RO (read\-only)\f[].
+All \f[B]action\f[R] function policies filter out branches which are
+mounted \f[B]read\-only\f[R] or tagged as \f[B]RO (read\-only)\f[R].
 .IP \[bu] 2
-All \f[B]create\f[] function policies filter out branches which are
-mounted \f[B]read\-only\f[], tagged \f[B]RO (read\-only)\f[] or \f[B]NC
-(no create)\f[], or has available space less than \f[C]minfreespace\f[].
+All \f[B]create\f[R] function policies filter out branches which are
+mounted \f[B]read\-only\f[R], tagged \f[B]RO (read\-only)\f[R] or
+\f[B]NC (no create)\f[R], or has available space less than
+\f[C]minfreespace\f[R].
 .PP
 Policies may have their own additional filtering such as those that
 require existing paths to be present.
 .PP
 If all branches are filtered an error will be returned.
-Typically \f[B]EROFS\f[] (read\-only filesystem) or \f[B]ENOSPC\f[] (no
-space left on device) depending on the most recent reason for filtering
-a branch.
-\f[B]ENOENT\f[] will be returned if no elegible branch is found.
+Typically \f[B]EROFS\f[R] (read\-only filesystem) or \f[B]ENOSPC\f[R]
+(no space left on device) depending on the most recent reason for
+filtering a branch.
+\f[B]ENOENT\f[R] will be returned if no eligible branch is found.
 .SS Path Preservation
 .PP
 Policies, as described below, are of two basic types.
-\f[C]path\ preserving\f[] and \f[C]non\-path\ preserving\f[].
+\f[C]path preserving\f[R] and \f[C]non\-path preserving\f[R].
 .PP
-All policies which start with \f[C]ep\f[] (\f[B]epff\f[],
-\f[B]eplfs\f[], \f[B]eplus\f[], \f[B]epmfs\f[], \f[B]eprand\f[]) are
-\f[C]path\ preserving\f[].
-\f[C]ep\f[] stands for \f[C]existing\ path\f[].
+All policies which start with \f[C]ep\f[R] (\f[B]epff\f[R],
+\f[B]eplfs\f[R], \f[B]eplus\f[R], \f[B]epmfs\f[R], \f[B]eprand\f[R]) are
+\f[C]path preserving\f[R].
+\f[C]ep\f[R] stands for \f[C]existing path\f[R].
 .PP
 A path preserving policy will only consider drives where the relative
 path being accessed already exists.
@@ -835,15 +834,15 @@ path being accessed already exists.
 When using non\-path preserving policies paths will be cloned to target
 drives as necessary.
 .PP
-With the \f[C]msp\f[] or \f[C]most\ shared\ path\f[] policies they are
-defined as \f[C]path\ preserving\f[] for the purpose of controlling
-\f[C]link\f[] and \f[C]rename\f[]\[aq]s behaviors since
-\f[C]ignorepponrename\f[] is available to disable that behavior.
+With the \f[C]msp\f[R] or \f[C]most shared path\f[R] policies they are
+defined as \f[C]path preserving\f[R] for the purpose of controlling
+\f[C]link\f[R] and \f[C]rename\f[R]\[cq]s behaviors since
+\f[C]ignorepponrename\f[R] is available to disable that behavior.
 In mergerfs v3.0 the path preserving behavior of rename and link will
 likely be separated from the policy all together.
 .SS Policy descriptions
 .PP
-A policy\[aq]s behavior differs, as mentioned above, based on the
+A policy\[cq]s behavior differs, as mentioned above, based on the
 function it is used with.
 Sometimes it really might not make sense to even offer certain policies
 because they are literally the same as others but it makes things a bit
@@ -852,7 +851,7 @@ In mergerfs 3.0 this might change.
 .PP
 .TS
 tab(@);
-lw(16.6n) lw(53.4n).
+lw(16.2n) lw(53.8n).
 T{
 Policy
 T}@T{
@@ -862,22 +861,22 @@ _
 T{
 all
 T}@T{
-Search: Same as \f[B]epall\f[].
-Action: Same as \f[B]epall\f[].
-Create: for \f[B]mkdir\f[], \f[B]mknod\f[], and \f[B]symlink\f[] it will
-apply to all branches.
-\f[B]create\f[] works like \f[B]ff\f[].
+Search: Same as \f[B]epall\f[R].
+Action: Same as \f[B]epall\f[R].
+Create: for \f[B]mkdir\f[R], \f[B]mknod\f[R], and \f[B]symlink\f[R] it
+will apply to all branches.
+\f[B]create\f[R] works like \f[B]ff\f[R].
 T}
 T{
 epall (existing path, all)
 T}@T{
-Search: Same as \f[B]epff\f[] (but more expensive because it doesn\[aq]t
-stop after finding a valid branch).
+Search: Same as \f[B]epff\f[R] (but more expensive because it
+doesn\[cq]t stop after finding a valid branch).
 Action: apply to all found.
-Create: for \f[B]mkdir\f[], \f[B]mknod\f[], and \f[B]symlink\f[] it will
-apply to all found.
-\f[B]create\f[] works like \f[B]epff\f[] (but more expensive because it
-doesn\[aq]t stop after finding a valid branch).
+Create: for \f[B]mkdir\f[R], \f[B]mknod\f[R], and \f[B]symlink\f[R] it
+will apply to all found.
+\f[B]create\f[R] works like \f[B]epff\f[R] (but more expensive because
+it doesn\[cq]t stop after finding a valid branch).
 T}
 T{
 epff (existing path, first found)
@@ -906,79 +905,79 @@ T}
 T{
 eppfrd (existing path, percentage free random distribution)
 T}@T{
-Like \f[B]pfrd\f[] but limited to existing paths.
+Like \f[B]pfrd\f[R] but limited to existing paths.
 T}
 T{
 eprand (existing path, random)
 T}@T{
-Calls \f[B]epall\f[] and then randomizes.
+Calls \f[B]epall\f[R] and then randomizes.
 Returns 1.
 T}
 T{
 erofs
 T}@T{
-Exclusively return \f[B]\-1\f[] with \f[B]errno\f[] set to
-\f[B]EROFS\f[] (read\-only filesystem).
+Exclusively return \f[B]\-1\f[R] with \f[B]errno\f[R] set to
+\f[B]EROFS\f[R] (read\-only filesystem).
 T}
 T{
 ff (first found)
 T}@T{
-Search: Same as \f[B]epff\f[].
-Action: Same as \f[B]epff\f[].
+Search: Same as \f[B]epff\f[R].
+Action: Same as \f[B]epff\f[R].
 Create: Given the order of the drives, as defined at mount time or
 configured at runtime, act on the first one found.
 T}
 T{
 lfs (least free space)
 T}@T{
-Search: Same as \f[B]eplfs\f[].
-Action: Same as \f[B]eplfs\f[].
+Search: Same as \f[B]eplfs\f[R].
+Action: Same as \f[B]eplfs\f[R].
 Create: Pick the drive with the least available free space.
 T}
 T{
 lus (least used space)
 T}@T{
-Search: Same as \f[B]eplus\f[].
-Action: Same as \f[B]eplus\f[].
+Search: Same as \f[B]eplus\f[R].
+Action: Same as \f[B]eplus\f[R].
 Create: Pick the drive with the least used space.
 T}
 T{
 mfs (most free space)
 T}@T{
-Search: Same as \f[B]epmfs\f[].
-Action: Same as \f[B]epmfs\f[].
+Search: Same as \f[B]epmfs\f[R].
+Action: Same as \f[B]epmfs\f[R].
 Create: Pick the drive with the most available free space.
 T}
 T{
 msplfs (most shared path, least free space)
 T}@T{
-Search: Same as \f[B]eplfs\f[].
-Action: Same as \f[B]eplfs\f[].
-Create: like \f[B]eplfs\f[] but walk back the path if it fails to find a
-branch at that level.
+Search: Same as \f[B]eplfs\f[R].
+Action: Same as \f[B]eplfs\f[R].
+Create: like \f[B]eplfs\f[R] but walk back the path if it fails to find
+a branch at that level.
 T}
 T{
 msplus (most shared path, least used space)
 T}@T{
-Search: Same as \f[B]eplus\f[].
-Action: Same as \f[B]eplus\f[].
-Create: like \f[B]eplus\f[] but walk back the path if it fails to find a
-branch at that level.
+Search: Same as \f[B]eplus\f[R].
+Action: Same as \f[B]eplus\f[R].
+Create: like \f[B]eplus\f[R] but walk back the path if it fails to find
+a branch at that level.
 T}
 T{
 mspmfs (most shared path, most free space)
 T}@T{
-Search: Same as \f[B]epmfs\f[].
-Action: Same as \f[B]epmfs\f[].
-Create: like \f[B]epmfs\f[] but walk back the path if it fails to find a
-branch at that level.
+Search: Same as \f[B]epmfs\f[R].
+Action: Same as \f[B]epmfs\f[R].
+Create: like \f[B]epmfs\f[R] but walk back the path if it fails to find
+a branch at that level.
 T}
 T{
 msppfrd (most shared path, percentage free random distribution)
 T}@T{
-Search: Same as \f[B]eppfrd\f[].
-Action: Same as \f[B]eppfrd\f[].
-Create: Like \f[B]eppfrd\f[] but will walk back the path if it fails to
+Search: Same as \f[B]eppfrd\f[R].
+Action: Same as \f[B]eppfrd\f[R].
+Create: Like \f[B]eppfrd\f[R] but will walk back the path if it fails to
 find a branch at that level.
 T}
 T{
@@ -989,27 +988,27 @@ T}
 T{
 pfrd (percentage free random distribution)
 T}@T{
-Search: Same as \f[B]eppfrd\f[].
-Action: Same as \f[B]eppfrd\f[].
+Search: Same as \f[B]eppfrd\f[R].
+Action: Same as \f[B]eppfrd\f[R].
 Create: Chooses a branch at random with the likelihood of selection
-based on a branch\[aq]s available space relative to the total.
+based on a branch\[cq]s available space relative to the total.
 T}
 T{
 rand (random)
 T}@T{
-Calls \f[B]all\f[] and then randomizes.
+Calls \f[B]all\f[R] and then randomizes.
 Returns 1.
 T}
 .TE
 .PP
-\f[B]NOTE:\f[] If you are using an underlying filesystem that reserves
+\f[B]NOTE:\f[R] If you are using an underlying filesystem that reserves
 blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the
-reservation by using \f[C]f_bavail\f[] (number of free blocks for
-unprivileged users) rather than \f[C]f_bfree\f[] (number of free blocks)
-in policy calculations.
-\f[B]df\f[] does NOT use \f[C]f_bavail\f[], it uses \f[C]f_bfree\f[], so
-direct comparisons between \f[B]df\f[] output and mergerfs\[aq] policies
-is not appropriate.
+reservation by using \f[C]f_bavail\f[R] (number of free blocks for
+unprivileged users) rather than \f[C]f_bfree\f[R] (number of free
+blocks) in policy calculations.
+\f[B]df\f[R] does NOT use \f[C]f_bavail\f[R], it uses \f[C]f_bfree\f[R],
+so direct comparisons between \f[B]df\f[R] output and mergerfs\[cq]
+policies is not appropriate.
 .SS Defaults
 .PP
 .TS
@@ -1039,26 +1038,27 @@ T}
 .TE
 .SS ioctl
 .PP
-When \f[C]ioctl\f[] is used with an open file then it will use the file
-handle which was created at the original \f[C]open\f[] call.
-However, when using \f[C]ioctl\f[] with a directory mergerfs will use
-the \f[C]open\f[] policy to find the directory to act on.
+When \f[C]ioctl\f[R] is used with an open file then it will use the file
+handle which was created at the original \f[C]open\f[R] call.
+However, when using \f[C]ioctl\f[R] with a directory mergerfs will use
+the \f[C]open\f[R] policy to find the directory to act on.
 .SS rename & link
 .PP
-\f[B]NOTE:\f[] If you\[aq]re receiving errors from software when files
+\f[B]NOTE:\f[R] If you\[cq]re receiving errors from software when files
 are moved / renamed / linked then you should consider changing the
-create policy to one which is \f[B]not\f[] path preserving, enabling
-\f[C]ignorepponrename\f[], or contacting the author of the offending
-software and requesting that \f[C]EXDEV\f[] (cross device / improper
+create policy to one which is \f[B]not\f[R] path preserving, enabling
+\f[C]ignorepponrename\f[R], or contacting the author of the offending
+software and requesting that \f[C]EXDEV\f[R] (cross device / improper
 link) be properly handled.
 .PP
-\f[C]rename\f[] and \f[C]link\f[] are tricky functions in a union
+\f[C]rename\f[R] and \f[C]link\f[R] are tricky functions in a union
 filesystem.
-\f[C]rename\f[] only works within a single filesystem or device.
-If a rename can\[aq]t be done atomically due to the source and
+\f[C]rename\f[R] only works within a single filesystem or device.
+If a rename can\[cq]t be done atomically due to the source and
 destination paths existing on different mount points it will return
-\f[B]\-1\f[] with \f[B]errno = EXDEV\f[] (cross device / improper link).
-So if a \f[C]rename\f[]\[aq]s source and target are on different drives
+\f[B]\-1\f[R] with \f[B]errno = EXDEV\f[R] (cross device / improper
+link).
+So if a \f[C]rename\f[R]\[cq]s source and target are on different drives
 within the pool it creates an issue.
 .PP
 Originally mergerfs would return EXDEV whenever a rename was requested
@@ -1068,21 +1068,22 @@ requirements.
 However, many applications fail to handle EXDEV at all and treat it as a
 normal error or otherwise handle it poorly.
 Such apps include: gvfsd\-fuse v1.20.3 and prior, Finder / CIFS/SMB
-client in Apple OSX 10.9+, NZBGet, Samba\[aq]s recycling bin feature.
+client in Apple OSX 10.9+, NZBGet, Samba\[cq]s recycling bin feature.
 .PP
 As a result a compromise was made in order to get most software to work
-while still obeying mergerfs\[aq] policies.
+while still obeying mergerfs\[cq] policies.
 Below is the basic logic.
 .IP \[bu] 2
-If using a \f[B]create\f[] policy which tries to preserve directory
+If using a \f[B]create\f[R] policy which tries to preserve directory
 paths (epff,eplfs,eplus,epmfs)
+.RS 2
 .IP \[bu] 2
-Using the \f[B]rename\f[] policy get the list of files to rename
+Using the \f[B]rename\f[R] policy get the list of files to rename
 .IP \[bu] 2
 For each file attempt rename:
 .RS 2
 .IP \[bu] 2
-If failure with ENOENT (no such file or directory) run \f[B]create\f[]
+If failure with ENOENT (no such file or directory) run \f[B]create\f[R]
 policy
 .IP \[bu] 2
 If create policy returns the same drive as currently evaluating then
@@ -1091,10 +1092,10 @@ clone the path
 Re\-attempt rename
 .RE
 .IP \[bu] 2
-If \f[B]any\f[] of the renames succeed the higher level rename is
+If \f[B]any\f[R] of the renames succeed the higher level rename is
 considered a success
 .IP \[bu] 2
-If \f[B]no\f[] renames succeed the first error encountered will be
+If \f[B]no\f[R] renames succeed the first error encountered will be
 returned
 .IP \[bu] 2
 On success:
@@ -1104,28 +1105,32 @@ Remove the target from all drives with no source file
 .IP \[bu] 2
 Remove the source from all drives which failed to rename
 .RE
+.RE
 .IP \[bu] 2
-If using a \f[B]create\f[] policy which does \f[B]not\f[] try to
+If using a \f[B]create\f[R] policy which does \f[B]not\f[R] try to
 preserve directory paths
+.RS 2
 .IP \[bu] 2
-Using the \f[B]rename\f[] policy get the list of files to rename
+Using the \f[B]rename\f[R] policy get the list of files to rename
 .IP \[bu] 2
-Using the \f[B]getattr\f[] policy get the target path
+Using the \f[B]getattr\f[R] policy get the target path
 .IP \[bu] 2
 For each file attempt rename:
 .RS 2
 .IP \[bu] 2
 If the source drive != target drive:
+.RS 2
 .IP \[bu] 2
 Clone target path from target drive to source drive
+.RE
 .IP \[bu] 2
 Rename
 .RE
 .IP \[bu] 2
-If \f[B]any\f[] of the renames succeed the higher level rename is
+If \f[B]any\f[R] of the renames succeed the higher level rename is
 considered a success
 .IP \[bu] 2
-If \f[B]no\f[] renames succeed the first error encountered will be
+If \f[B]no\f[R] renames succeed the first error encountered will be
 returned
 .IP \[bu] 2
 On success:
@@ -1135,13 +1140,14 @@ Remove the target from all drives with no source file
 .IP \[bu] 2
 Remove the source from all drives which failed to rename
 .RE
+.RE
 .PP
 The the removals are subject to normal entitlement checks.
 .PP
 The above behavior will help minimize the likelihood of EXDEV being
 returned but it will still be possible.
 .PP
-\f[B]link\f[] uses the same strategy but without the removals.
+\f[B]link\f[R] uses the same strategy but without the removals.
 .SS readdir
 .PP
 readdir (http://linux.die.net/man/3/readdir) is different from all other
@@ -1150,11 +1156,11 @@ While it could have its own set of policies to tweak its behavior at
 this time it provides a simple union of files and directories found.
 Remember that any action or information queried about these files and
 directories come from the respective function.
-For instance: an \f[B]ls\f[] is a \f[B]readdir\f[] and for each
-file/directory returned \f[B]getattr\f[] is called.
-Meaning the policy of \f[B]getattr\f[] is responsible for choosing the
+For instance: an \f[B]ls\f[R] is a \f[B]readdir\f[R] and for each
+file/directory returned \f[B]getattr\f[R] is called.
+Meaning the policy of \f[B]getattr\f[R] is responsible for choosing the
 file/directory which is the source of the metadata you see in an
-\f[B]ls\f[].
+\f[B]ls\f[R].
 .SS statfs / statvfs
 .PP
 statvfs (http://linux.die.net/man/2/statvfs) normalizes the source
@@ -1165,10 +1171,10 @@ Total, used, and free.
 The sources however are dedupped based on the drive so multiple sources
 on the same drive will not result in double counting its space.
 Filesystems mounted further down the tree of the branch will not be
-included when checking the mount\[aq]s stats.
+included when checking the mount\[cq]s stats.
 .PP
-The options \f[C]statfs\f[] and \f[C]statfs_ignore\f[] can be used to
-modify \f[C]statfs\f[] behavior.
+The options \f[C]statfs\f[R] and \f[C]statfs_ignore\f[R] can be used to
+modify \f[C]statfs\f[R] behavior.
 .SH ERROR HANDLING
 .PP
 POSIX filesystem functions offer a single return code meaning that there
@@ -1205,47 +1211,47 @@ For search functions there is always a single thing acted on and as such
 whatever return value that comes from the single function call is
 returned.
 .PP
-For create functions \f[C]mkdir\f[], \f[C]mknod\f[], and
-\f[C]symlink\f[] which don\[aq]t return a file descriptor and therefore
-can have \f[C]all\f[] or \f[C]epall\f[] policies it will return success
-if any of the calls succeed and an error otherwise.
+For create functions \f[C]mkdir\f[R], \f[C]mknod\f[R], and
+\f[C]symlink\f[R] which don\[cq]t return a file descriptor and therefore
+can have \f[C]all\f[R] or \f[C]epall\f[R] policies it will return
+success if any of the calls succeed and an error otherwise.
 .SH BUILD / UPDATE
 .PP
-\f[B]NOTE:\f[] Prebuilt packages can be found at and recommended for
-most users: https://github.com/trapexit/mergerfs/releases \f[B]NOTE:\f[]
-Only tagged releases are supported.
-\f[C]master\f[] and other branches should be considered works in
+\f[B]NOTE:\f[R] Prebuilt packages can be found at and recommended for
+most users: https://github.com/trapexit/mergerfs/releases
+\f[B]NOTE:\f[R] Only tagged releases are supported.
+\f[C]master\f[R] and other branches should be considered works in
 progress.
 .PP
 First get the code from github (https://github.com/trapexit/mergerfs).
 .IP
 .nf
 \f[C]
-$\ git\ clone\ https://github.com/trapexit/mergerfs.git
-$\ #\ or
-$\ wget\ https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs\-<ver>.tar.gz
-\f[]
+$ git clone https://github.com/trapexit/mergerfs.git
+$ # or
+$ wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs\-<ver>.tar.gz
+\f[R]
 .fi
 .SS Debian / Ubuntu
 .IP
 .nf
 \f[C]
-$\ cd\ mergerfs
-$\ sudo\ tools/install\-build\-pkgs
-$\ make\ deb
-$\ sudo\ dpkg\ \-i\ ../mergerfs_<version>_<arch>.deb
-\f[]
+$ cd mergerfs
+$ sudo tools/install\-build\-pkgs
+$ make deb
+$ sudo dpkg \-i ../mergerfs_<version>_<arch>.deb
+\f[R]
 .fi
 .SS RHEL / CentOS /Fedora
 .IP
 .nf
 \f[C]
-$\ su\ \-
-#\ cd\ mergerfs
-#\ tools/install\-build\-pkgs
-#\ make\ rpm
-#\ rpm\ \-i\ rpmbuild/RPMS/<arch>/mergerfs\-<version>.<arch>.rpm
-\f[]
+$ su \-
+# cd mergerfs
+# tools/install\-build\-pkgs
+# make rpm
+# rpm \-i rpmbuild/RPMS/<arch>/mergerfs\-<version>.<arch>.rpm
+\f[R]
 .fi
 .SS Generically
 .PP
@@ -1253,22 +1259,22 @@ Have git, g++, make, python installed.
 .IP
 .nf
 \f[C]
-$\ cd\ mergerfs
-$\ make
-$\ sudo\ make\ install
-\f[]
+$ cd mergerfs
+$ make
+$ sudo make install
+\f[R]
 .fi
 .SS Build options
 .IP
 .nf
 \f[C]
-$\ make\ help
-usage:\ make
+$ make help
+usage: make
 
-make\ USE_XATTR=0\ \ \ \ \ \ \-\ build\ program\ without\ xattrs\ functionality
-make\ STATIC=1\ \ \ \ \ \ \ \ \ \-\ build\ static\ binary
-make\ LTO=1\ \ \ \ \ \ \ \ \ \ \ \ \-\ build\ with\ link\ time\ optimization
-\f[]
+make USE_XATTR=0      \- build program without xattrs functionality
+make STATIC=1         \- build static binary
+make LTO=1            \- build with link time optimization
+\f[R]
 .fi
 .SH UPGRADE
 .PP
@@ -1277,123 +1283,58 @@ instance.
 Simply install the new version of mergerfs and follow the instructions
 below.
 .PP
-Add \f[C]nonempty\f[] to your mergerfs option list and call mergerfs
-again or if using \f[C]/etc/fstab\f[] call for it to mount again.
+Add \f[C]nonempty\f[R] to your mergerfs option list and call mergerfs
+again or if using \f[C]/etc/fstab\f[R] call for it to mount again.
 Existing open files and such will continue to work fine though they
-won\[aq]t see runtime changes since any such change would be the new
+won\[cq]t see runtime changes since any such change would be the new
 mount.
 If you plan on changing settings with the new mount you should / could
 apply those before mounting the new version.
 .IP
 .nf
 \f[C]
-$\ sudo\ mount\ /mnt/mergerfs
-$\ mount\ |\ grep\ mergerfs
-media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
-media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
-\f[]
+$ sudo mount /mnt/mergerfs
+$ mount | grep mergerfs
+media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
+media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
+\f[R]
 .fi
 .PP
 A problem with this approach is that the underlying instance will
 continue to run even if the software using it stop or are restarted.
-To work around this you can use a "lazy umount".
+To work around this you can use a \[lq]lazy umount\[rq].
 Before mounting over top the mount point with the new instance of
-mergerfs issue: \f[C]umount\ \-l\ <mergerfs_mountpoint>\f[].
+mergerfs issue: \f[C]umount \-l <mergerfs_mountpoint>\f[R].
 .SH RUNTIME CONFIG
-.SS ioctl
-.PP
-The original runtime config API was via xattr calls.
-This however became an issue when needing to disable xattr.
-While slightly less convenient ioctl does not have the same problems and
-will be the main API going forward.
-.PP
-The keys are the same as the command line option arguments as well as
-the config file.
-.SS requests / commands
-.PP
-All commands take a 4096 byte char buffer.
-.IP \[bu] 2
-read keys: get a nul \[aq]\[aq] delimited list of option keys
-.IP \[bu] 2
-_IOWR(0xDF,0,char[4096]) = 0xD000DF00
-.IP \[bu] 2
-on success ioctl return value is the total length
-.IP \[bu] 2
-read value: get an option value
-.IP \[bu] 2
-_IOWR(0xDF,1,char[4096]) = 0xD000DF01
-.IP \[bu] 2
-the key is passed in via the char buffer as a nul \[aq]\[aq] terminated
-string
-.IP \[bu] 2
-on success ioctl return value is the total length
-.IP \[bu] 2
-write value: set an option value
-.IP \[bu] 2
-_IOW(0xDF,2,char[4096]) = 0x5000DF02
-.IP \[bu] 2
-the key and value is passed in via the char buffer as a nul \[aq]\[aq]
-terminated string in the format of \f[C]key=value\f[]
-.IP \[bu] 2
-on success ioctl return value is 0
-.IP \[bu] 2
-file info: get mergerfs metadata info for a file
-.IP \[bu] 2
-_IOWR(0xDF,3,char[4096]) = 0xD000DF03
-.IP \[bu] 2
-the key is passed in via the char buffer as a nul \[aq]\[aq] terminated
-string
-.IP \[bu] 2
-on success the ioctl return value is the total length
-.IP \[bu] 2
-keys:
-.RS 2
-.IP \[bu] 2
-basepath: the base mount point for the file according to the getattr
-policy
-.IP \[bu] 2
-relpath: the relative path of the file from the mount point
-.IP \[bu] 2
-fullpath: the full path of the underlying file according to the getattr
-policy
-.IP \[bu] 2
-allpaths: a NUL \[aq]\[aq] delimited list of full paths to all files
-found
-.RE
-.SS .mergerfs pseudo file (deprecated)
-.PP
-NOTE: this interface will be removed in mergerfs 3.0
+.SS .mergerfs pseudo file
 .IP
 .nf
 \f[C]
 <mountpoint>/.mergerfs
-\f[]
+\f[R]
 .fi
 .PP
 There is a pseudo file available at the mount point which allows for the
-runtime modification of certain \f[B]mergerfs\f[] options.
-The file will not show up in \f[B]readdir\f[] but can be
-\f[B]stat\f[]\[aq]ed and manipulated via
+runtime modification of certain \f[B]mergerfs\f[R] options.
+The file will not show up in \f[B]readdir\f[R] but can be
+\f[B]stat\f[R]\[cq]ed and manipulated via
 {list,get,set}xattrs (http://linux.die.net/man/2/listxattr) calls.
 .PP
-Any changes made at runtime are \f[B]not\f[] persisted.
+Any changes made at runtime are \f[B]not\f[R] persisted.
 If you wish for values to persist they must be included as options
 wherever you configure the mounting of mergerfs (/etc/fstab).
 .SS Keys
 .PP
-Use \f[C]xattr\ \-l\ /mountpoint/.mergerfs\f[] to see all supported
-keys.
+Use \f[C]getfattr \-d /mountpoint/.mergerfs\f[R] or
+\f[C]xattr \-l /mountpoint/.mergerfs\f[R] to see all supported keys.
 Some are informational and therefore read\-only.
-\f[C]setxattr\f[] will return EINVAL (invalid argument) on read\-only
+\f[C]setxattr\f[R] will return EINVAL (invalid argument) on read\-only
 keys.
 .SS Values
 .PP
 Same as the command line.
 .SS user.mergerfs.branches
 .PP
-\f[B]NOTE:\f[] formerly \f[C]user.mergerfs.srcmounts\f[] but said key is
-still supported.
-.PP
 Used to query or modify the list of branches.
 When modifying there are several shortcuts to easy manipulation of the
 list.
@@ -1439,79 +1380,51 @@ remove last in list
 T}
 .TE
 .PP
-\f[C]xattr\ \-w\ user.mergerfs.branches\ +</mnt/drive3\ /mnt/pool/.mergerfs\f[]
+\f[C]xattr \-w user.mergerfs.branches +</mnt/drive3 /mnt/pool/.mergerfs\f[R]
 .PP
-The \f[C]=NC\f[], \f[C]=RO\f[], \f[C]=RW\f[] syntax works just as on the
-command line.
+The \f[C]=NC\f[R], \f[C]=RO\f[R], \f[C]=RW\f[R] syntax works just as on
+the command line.
 .SS Example
 .IP
 .nf
 \f[C]
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-l\ .mergerfs
-user.mergerfs.branches:\ /mnt/a=RW:/mnt/b=RW
-user.mergerfs.minfreespace:\ 4294967295
-user.mergerfs.moveonenospc:\ false
+[trapexit:/mnt/mergerfs] $ getfattr \-d .mergerfs
+user.mergerfs.branches=\[dq]/mnt/a=RW:/mnt/b=RW\[dq]
+user.mergerfs.minfreespace=\[dq]4294967295\[dq]
+user.mergerfs.moveonenospc=\[dq]false\[dq]
 \&...
 
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.category.search\ .mergerfs
-ff
-
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.category.search\ newest\ .mergerfs
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.category.search\ .mergerfs
-newest
-
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.branches\ +/mnt/c\ .mergerfs
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.branches\ .mergerfs
-/mnt/a:/mnt/b:/mnt/c
+[trapexit:/mnt/mergerfs] $ getfattr \-n user.mergerfs.category.search .mergerfs
+user.mergerfs.category.search=\[dq]ff\[dq]
 
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.branches\ =/mnt/c\ .mergerfs
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.branches\ .mergerfs
-/mnt/c
-
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.branches\ \[aq]+</mnt/a:/mnt/b\[aq]\ .mergerfs
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.branches\ .mergerfs
-/mnt/a:/mnt/b:/mnt/c
-\f[]
+[trapexit:/mnt/mergerfs] $ setfattr \-n user.mergerfs.category.search \-v newest .mergerfs
+[trapexit:/mnt/mergerfs] $ getfattr \-n user.mergerfs.category.search .mergerfs
+user.mergerfs.category.search=\[dq]newest\[dq]
+\f[R]
 .fi
 .SS file / directory xattrs
 .PP
-While they won\[aq]t show up when using
-listxattr (http://linux.die.net/man/2/listxattr) \f[B]mergerfs\f[]
-offers a number of special xattrs to query information about the files
-served.
+While they won\[cq]t show up when using \f[C]getfattr\f[R]
+\f[B]mergerfs\f[R] offers a number of special xattrs to query
+information about the files served.
 To access the values you will need to issue a
 getxattr (http://linux.die.net/man/2/getxattr) for one of the following:
 .IP \[bu] 2
-\f[B]user.mergerfs.basepath\f[]: the base mount point for the file given
-the current getattr policy
+\f[B]user.mergerfs.basepath\f[R]: the base mount point for the file
+given the current getattr policy
 .IP \[bu] 2
-\f[B]user.mergerfs.relpath\f[]: the relative path of the file from the
+\f[B]user.mergerfs.relpath\f[R]: the relative path of the file from the
 perspective of the mount point
 .IP \[bu] 2
-\f[B]user.mergerfs.fullpath\f[]: the full path of the original file
+\f[B]user.mergerfs.fullpath\f[R]: the full path of the original file
 given the getattr policy
 .IP \[bu] 2
-\f[B]user.mergerfs.allpaths\f[]: a NUL (\[aq]\[aq]) separated list of
+\f[B]user.mergerfs.allpaths\f[R]: a NUL (`\[rs]0') separated list of
 full paths to all files found
-.IP
-.nf
-\f[C]
-[trapexit:/mnt/mergerfs]\ $\ ls
-A\ B\ C
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.fullpath\ A
-/mnt/a/full/path/to/A
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.basepath\ A
-/mnt/a
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.relpath\ A
-/full/path/to/A
-[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.allpaths\ A\ |\ tr\ \[aq]\\0\[aq]\ \[aq]\\n\[aq]
-/mnt/a/full/path/to/A
-/mnt/b/full/path/to/A
-\f[]
-.fi
 .SH TOOLING
 .IP \[bu] 2
 https://github.com/trapexit/mergerfs\-tools
+.RS 2
 .IP \[bu] 2
 mergerfs.ctl: A tool to make it easier to query and configure mergerfs
 at runtime
@@ -1529,16 +1442,21 @@ most filled to the least filled
 .IP \[bu] 2
 mergerfs.consolidate: move files within a single mergerfs directory to
 the drive with most free space
+.RE
 .IP \[bu] 2
 https://github.com/trapexit/scorch
+.RS 2
 .IP \[bu] 2
 scorch: A tool to help discover silent corruption of files and keep
 track of files
+.RE
 .IP \[bu] 2
 https://github.com/trapexit/bbf
+.RS 2
 .IP \[bu] 2
-bbf (bad block finder): a tool to scan for and \[aq]fix\[aq] hard drive
-bad blocks and find the files using those blocks
+bbf (bad block finder): a tool to scan for and `fix' hard drive bad
+blocks and find the files using those blocks
+.RE
 .SH CACHING
 .SS page caching
 .PP
@@ -1553,39 +1471,39 @@ Underlying files cached, mergerfs files cached across opens.
 * cache.files=auto\-full: Enables page caching.
 Underlying files cached, mergerfs files cached across opens if mtime and
 size are unchanged since previous open.
-* cache.files=libfuse: follow traditional libfuse \f[C]direct_io\f[],
-\f[C]kernel_cache\f[], and \f[C]auto_cache\f[] arguments.
+* cache.files=libfuse: follow traditional libfuse \f[C]direct_io\f[R],
+\f[C]kernel_cache\f[R], and \f[C]auto_cache\f[R] arguments.
 .PP
 FUSE, which mergerfs uses, offers a number of page caching modes.
-mergerfs tries to simplify their use via the \f[C]cache.files\f[]
+mergerfs tries to simplify their use via the \f[C]cache.files\f[R]
 option.
-It can and should replace usage of \f[C]direct_io\f[],
-\f[C]kernel_cache\f[], and \f[C]auto_cache\f[].
+It can and should replace usage of \f[C]direct_io\f[R],
+\f[C]kernel_cache\f[R], and \f[C]auto_cache\f[R].
 .PP
 Due to mergerfs using FUSE and therefore being a userland process
 proxying existing filesystems the kernel will double cache the content
 being read and written through mergerfs.
 Once from the underlying filesystem and once from mergerfs (it sees them
 as two separate entities).
-Using \f[C]cache.files=off\f[] will keep the double caching from
+Using \f[C]cache.files=off\f[R] will keep the double caching from
 happening by disabling caching of mergerfs but this has the side effect
-that \f[I]all\f[] read and write calls will be passed to mergerfs which
-may be slower than enabling caching, you lose shared \f[C]mmap\f[]
+that \f[I]all\f[R] read and write calls will be passed to mergerfs which
+may be slower than enabling caching, you lose shared \f[C]mmap\f[R]
 support which can affect apps such as rtorrent, and no read\-ahead will
 take place.
 The kernel will still cache the underlying filesystem data but that only
 helps so much given mergerfs will still process all requests.
 .PP
 If you do enable file page caching,
-\f[C]cache.files=partial|full|auto\-full\f[], you should also enable
-\f[C]dropcacheonclose\f[] which will cause mergerfs to instruct the
-kernel to flush the underlying file\[aq]s page cache when the file is
+\f[C]cache.files=partial|full|auto\-full\f[R], you should also enable
+\f[C]dropcacheonclose\f[R] which will cause mergerfs to instruct the
+kernel to flush the underlying file\[cq]s page cache when the file is
 closed.
 This behavior is the same as the rsync fadvise / drop cache patch and
-Feh\[aq]s nocache project.
+Feh\[cq]s nocache project.
 .PP
 If most files are read once through and closed (like media) it is best
-to enable \f[C]dropcacheonclose\f[] regardless of caching mode in order
+to enable \f[C]dropcacheonclose\f[R] regardless of caching mode in order
 to minimize buffer bloat.
 .PP
 It is difficult to balance memory usage, cache bloat & duplication, and
@@ -1593,7 +1511,7 @@ performance.
 Ideally mergerfs would be able to disable caching for the files it
 reads/writes but allow page caching for itself.
 That would limit the FUSE overhead.
-However, there isn\[aq]t a good way to achieve this.
+However, there isn\[cq]t a good way to achieve this.
 It would need to open all files with O_DIRECT which places limitations
 on the what underlying filesystems would be supported and complicates
 the code.
@@ -1605,23 +1523,23 @@ https://www.kernel.org/doc/Documentation/filesystems/fuse\-io.txt
 Given the relatively high cost of FUSE due to the kernel <\-> userspace
 round trips there are kernel side caches for file entries and
 attributes.
-The entry cache limits the \f[C]lookup\f[] calls to mergerfs which ask
+The entry cache limits the \f[C]lookup\f[R] calls to mergerfs which ask
 if a file exists.
-The attribute cache limits the need to make \f[C]getattr\f[] calls to
+The attribute cache limits the need to make \f[C]getattr\f[R] calls to
 mergerfs which provide file attributes (mode, size, type, etc.).
 As with the page cache these should not be used if the underlying
 filesystems are being manipulated at the same time as it could lead to
 odd behavior or data corruption.
-The options for setting these are \f[C]cache.entry\f[] and
-\f[C]cache.negative_entry\f[] for the entry cache and
-\f[C]cache.attr\f[] for the attributes cache.
-\f[C]cache.negative_entry\f[] refers to the timeout for negative
+The options for setting these are \f[C]cache.entry\f[R] and
+\f[C]cache.negative_entry\f[R] for the entry cache and
+\f[C]cache.attr\f[R] for the attributes cache.
+\f[C]cache.negative_entry\f[R] refers to the timeout for negative
 responses to lookups (non\-existent files).
 .SS writeback caching
 .PP
-When \f[C]cache.files\f[] is enabled the default is for it to perform
+When \f[C]cache.files\f[R] is enabled the default is for it to perform
 writethrough caching.
-This behavior won\[aq]t help improve performance as each write still
+This behavior won\[cq]t help improve performance as each write still
 goes one for one through the filesystem.
 By enabling the FUSE writeback cache small writes may be aggregated by
 the kernel and then sent to mergerfs as one larger request.
@@ -1629,15 +1547,15 @@ This can greatly improve the throughput for apps which write to files
 inefficiently.
 The amount the kernel can aggregate is limited by the size of a FUSE
 message.
-Read the \f[C]fuse_msg_size\f[] section for more details.
+Read the \f[C]fuse_msg_size\f[R] section for more details.
 .PP
 There is a small side effect as a result of enabling writeback caching.
-Underlying files won\[aq]t ever be opened with O_APPEND or O_WRONLY.
+Underlying files won\[cq]t ever be opened with O_APPEND or O_WRONLY.
 The former because the kernel then manages append mode and the latter
 because the kernel may request file data from mergerfs to populate the
 write cache.
 The O_APPEND change means that if a file is changed outside of mergerfs
-it could lead to corruption as the kernel won\[aq]t know the end of the
+it could lead to corruption as the kernel won\[cq]t know the end of the
 file has changed.
 That said any time you use caching you should keep from using the same
 file outside of mergerfs at the same time.
@@ -1650,62 +1568,64 @@ on older kernels, 1M on newer).
 .PP
 Policies are run every time a function (with a policy as mentioned
 above) is called.
-These policies can be expensive depending on mergerfs\[aq] setup and
+These policies can be expensive depending on mergerfs\[cq] setup and
 client usage patterns.
-Generally we wouldn\[aq]t want to cache policy results because it may
+Generally we wouldn\[cq]t want to cache policy results because it may
 result in stale responses if the underlying drives are used directly.
 .PP
-The \f[C]open\f[] policy cache will cache the result of an \f[C]open\f[]
-policy for a particular input for \f[C]cache.open\f[] seconds or until
-the file is unlinked.
+The \f[C]open\f[R] policy cache will cache the result of an
+\f[C]open\f[R] policy for a particular input for \f[C]cache.open\f[R]
+seconds or until the file is unlinked.
 Each file close (release) will randomly chose to clean up the cache of
 expired entries.
 .PP
 This cache is really only useful in cases where you have a large number
-of branches and \f[C]open\f[] is called on the same files repeatedly
-(like \f[B]Transmission\f[] which opens and closes a file on every
+of branches and \f[C]open\f[R] is called on the same files repeatedly
+(like \f[B]Transmission\f[R] which opens and closes a file on every
 read/write presumably to keep file handle usage low).
 .SS statfs caching
 .PP
-Of the syscalls used by mergerfs in policies the \f[C]statfs\f[] /
-\f[C]statvfs\f[] call is perhaps the most expensive.
-It\[aq]s used to find out the available space of a drive and whether it
+Of the syscalls used by mergerfs in policies the \f[C]statfs\f[R] /
+\f[C]statvfs\f[R] call is perhaps the most expensive.
+It\[cq]s used to find out the available space of a drive and whether it
 is mounted read\-only.
 Depending on the setup and usage pattern these queries can be relatively
 costly.
-When \f[C]cache.statfs\f[] is enabled all calls to \f[C]statfs\f[] by a
-policy will be cached for the number of seconds its set to.
+When \f[C]cache.statfs\f[R] is enabled all calls to \f[C]statfs\f[R] by
+a policy will be cached for the number of seconds its set to.
 .PP
-Example: If the create policy is \f[C]mfs\f[] and the timeout is 60 then
-for that 60 seconds the same drive will be returned as the target for
-creates because the available space won\[aq]t be updated for that time.
+Example: If the create policy is \f[C]mfs\f[R] and the timeout is 60
+then for that 60 seconds the same drive will be returned as the target
+for creates because the available space won\[cq]t be updated for that
+time.
 .SS symlink caching
 .PP
 As of version 4.20 Linux supports symlink caching.
 Significant performance increases can be had in workloads which use a
 lot of symlinks.
-Setting \f[C]cache.symlinks=true\f[] will result in requesting symlink
+Setting \f[C]cache.symlinks=true\f[R] will result in requesting symlink
 caching from the kernel only if supported.
 As a result its safe to enable it on systems prior to 4.20.
 That said it is disabled by default for now.
 You can see if caching is enabled by querying the xattr
-\f[C]user.mergerfs.cache.symlinks\f[] but given it must be requested at
+\f[C]user.mergerfs.cache.symlinks\f[R] but given it must be requested at
 startup you can not change it at runtime.
 .SS readdir caching
 .PP
 As of version 4.20 Linux supports readdir caching.
 This can have a significant impact on directory traversal.
-Especially when combined with entry (\f[C]cache.entry\f[]) and attribute
-(\f[C]cache.attr\f[]) caching.
-Setting \f[C]cache.readdir=true\f[] will result in requesting readdir
-caching from the kernel on each \f[C]opendir\f[].
-If the kernel doesn\[aq]t support readdir caching setting the option to
-\f[C]true\f[] has no effect.
+Especially when combined with entry (\f[C]cache.entry\f[R]) and
+attribute (\f[C]cache.attr\f[R]) caching.
+Setting \f[C]cache.readdir=true\f[R] will result in requesting readdir
+caching from the kernel on each \f[C]opendir\f[R].
+If the kernel doesn\[cq]t support readdir caching setting the option to
+\f[C]true\f[R] has no effect.
 This option is configurable at runtime via xattr
-\f[C]user.mergerfs.cache.readdir\f[].
+\f[C]user.mergerfs.cache.readdir\f[R].
 .SS tiered caching
 .PP
-Some storage technologies support what some call "tiered" caching.
+Some storage technologies support what some call \[lq]tiered\[rq]
+caching.
 The placing of usually smaller, faster storage as a transparent cache to
 larger, slower storage.
 NVMe, SSD, Optane in front of traditional HDDs for instance.
@@ -1716,18 +1636,18 @@ complicate the code.
 However, there are a few situations where a cache drive could help with
 a typical mergerfs setup.
 .IP "1." 3
-Fast network, slow drives, many readers: You\[aq]ve a 10+Gbps network
-with many readers and your regular drives can\[aq]t keep up.
+Fast network, slow drives, many readers: You\[cq]ve a 10+Gbps network
+with many readers and your regular drives can\[cq]t keep up.
 .IP "2." 3
-Fast network, slow drives, small\[aq]ish bursty writes: You have a
+Fast network, slow drives, small\[cq]ish bursty writes: You have a
 10+Gbps network and wish to transfer amounts of data less than your
 cache drive but wish to do so quickly.
 .PP
-With #1 its arguable if you should be using mergerfs at all.
+With #1 it\[cq]s arguable if you should be using mergerfs at all.
 RAID would probably be the better solution.
-If you\[aq]re going to use mergerfs there are other tactics that may
+If you\[cq]re going to use mergerfs there are other tactics that may
 help: spreading the data across drives (see the mergerfs.dup tool) and
-setting \f[C]func.open=rand\f[], using \f[C]symlinkify\f[], or using
+setting \f[C]func.open=rand\f[R], using \f[C]symlinkify\f[R], or using
 dm\-cache or a similar technology to add tiered cache to the underlying
 device.
 .PP
@@ -1738,46 +1658,47 @@ Create 2 mergerfs pools.
 One which includes just the slow drives and one which has both the fast
 drives (SSD,NVME,etc.) and slow drives.
 .IP "2." 3
-The \[aq]cache\[aq] pool should have the cache drives listed first.
+The `cache' pool should have the cache drives listed first.
 .IP "3." 3
-The best \f[C]create\f[] policies to use for the \[aq]cache\[aq] pool
-would probably be \f[C]ff\f[], \f[C]epff\f[], \f[C]lfs\f[], or
-\f[C]eplfs\f[].
+The best \f[C]create\f[R] policies to use for the `cache' pool would
+probably be \f[C]ff\f[R], \f[C]epff\f[R], \f[C]lfs\f[R], or
+\f[C]eplfs\f[R].
 The latter two under the assumption that the cache drive(s) are far
 smaller than the backing drives.
-If using path preserving policies remember that you\[aq]ll need to
+If using path preserving policies remember that you\[cq]ll need to
 manually create the core directories of those paths you wish to be
 cached.
 Be sure the permissions are in sync.
-Use \f[C]mergerfs.fsck\f[] to check / correct them.
-You could also tag the slow drives as \f[C]=NC\f[] though that\[aq]d
-mean if the cache drives fill you\[aq]d get "out of space" errors.
+Use \f[C]mergerfs.fsck\f[R] to check / correct them.
+You could also tag the slow drives as \f[C]=NC\f[R] though that\[cq]d
+mean if the cache drives fill you\[cq]d get \[lq]out of space\[rq]
+errors.
 .IP "4." 3
-Enable \f[C]moveonenospc\f[] and set \f[C]minfreespace\f[]
+Enable \f[C]moveonenospc\f[R] and set \f[C]minfreespace\f[R]
 appropriately.
-To make sure there is enough room on the "slow" pool you might want to
-set \f[C]minfreespace\f[] to at least as large as the size of the
-largest cache drive if not larger.
+To make sure there is enough room on the \[lq]slow\[rq] pool you might
+want to set \f[C]minfreespace\f[R] to at least as large as the size of
+the largest cache drive if not larger.
 This way in the worst case the whole of the cache drive(s) can be moved
 to the other drives.
 .IP "5." 3
 Set your programs to use the cache pool.
 .IP "6." 3
-Save one of the below scripts or create you\[aq]re own.
+Save one of the below scripts or create you\[cq]re own.
 .IP "7." 3
-Use \f[C]cron\f[] (as root) to schedule the command at whatever
+Use \f[C]cron\f[R] (as root) to schedule the command at whatever
 frequency is appropriate for your workflow.
 .SS time based expiring
 .PP
 Move files from cache to backing pool based only on the last time the
 file was accessed.
-Replace \f[C]\-atime\f[] with \f[C]\-amin\f[] if you want minutes rather
-than days.
-May want to use the \f[C]fadvise\f[] / \f[C]\-\-drop\-cache\f[] version
-of rsync or run rsync with the tool "nocache".
+Replace \f[C]\-atime\f[R] with \f[C]\-amin\f[R] if you want minutes
+rather than days.
+May want to use the \f[C]fadvise\f[R] / \f[C]\-\-drop\-cache\f[R]
+version of rsync or run rsync with the tool \[lq]nocache\[rq].
 .PP
-\f[I]NOTE:\f[] The arguments to these scripts include the cache
-\f[B]drive\f[].
+\f[I]NOTE:\f[R] The arguments to these scripts include the cache
+\f[B]drive\f[R].
 Not the pool with the cache drive.
 You could have data loss if the source is the cache pool.
 .IP
@@ -1785,26 +1706,26 @@ You could have data loss if the source is the cache pool.
 \f[C]
 #!/bin/bash
 
-if\ [\ $#\ !=\ 3\ ];\ then
-\ \ echo\ "usage:\ $0\ <cache\-drive>\ <backing\-pool>\ <days\-old>"
-\ \ exit\ 1
+if [ $# != 3 ]; then
+  echo \[dq]usage: $0 <cache\-drive> <backing\-pool> <days\-old>\[dq]
+  exit 1
 fi
 
-CACHE="${1}"
-BACKING="${2}"
+CACHE=\[dq]${1}\[dq]
+BACKING=\[dq]${2}\[dq]
 N=${3}
 
-find\ "${CACHE}"\ \-type\ f\ \-atime\ +${N}\ \-printf\ \[aq]%P\\n\[aq]\ |\ \\
-\ \ rsync\ \-\-files\-from=\-\ \-axqHAXWES\ \-\-preallocate\ \-\-remove\-source\-files\ "${CACHE}/"\ "${BACKING}/"
-\f[]
+find \[dq]${CACHE}\[dq] \-type f \-atime +${N} \-printf \[aq]%P\[rs]n\[aq] | \[rs]
+  rsync \-\-files\-from=\- \-axqHAXWES \-\-preallocate \-\-remove\-source\-files \[dq]${CACHE}/\[dq] \[dq]${BACKING}/\[dq]
+\f[R]
 .fi
 .SS percentage full expiring
 .PP
 Move the oldest file from the cache to the backing pool.
 Continue till below percentage threshold.
 .PP
-\f[I]NOTE:\f[] The arguments to these scripts include the cache
-\f[B]drive\f[].
+\f[I]NOTE:\f[R] The arguments to these scripts include the cache
+\f[B]drive\f[R].
 Not the pool with the cache drive.
 You could have data loss if the source is the cache pool.
 .IP
@@ -1812,26 +1733,26 @@ You could have data loss if the source is the cache pool.
 \f[C]
 #!/bin/bash
 
-if\ [\ $#\ !=\ 3\ ];\ then
-\ \ echo\ "usage:\ $0\ <cache\-drive>\ <backing\-pool>\ <percentage>"
-\ \ exit\ 1
+if [ $# != 3 ]; then
+  echo \[dq]usage: $0 <cache\-drive> <backing\-pool> <percentage>\[dq]
+  exit 1
 fi
 
-CACHE="${1}"
-BACKING="${2}"
+CACHE=\[dq]${1}\[dq]
+BACKING=\[dq]${2}\[dq]
 PERCENTAGE=${3}
 
-set\ \-o\ errexit
-while\ [\ $(df\ \-\-output=pcent\ "${CACHE}"\ |\ grep\ \-v\ Use\ |\ cut\ \-d\[aq]%\[aq]\ \-f1)\ \-gt\ ${PERCENTAGE}\ ]
+set \-o errexit
+while [ $(df \-\-output=pcent \[dq]${CACHE}\[dq] | grep \-v Use | cut \-d\[aq]%\[aq] \-f1) \-gt ${PERCENTAGE} ]
 do
-\ \ \ \ FILE=$(find\ "${CACHE}"\ \-type\ f\ \-printf\ \[aq]%A\@\ %P\\n\[aq]\ |\ \\
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ sort\ |\ \\
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ head\ \-n\ 1\ |\ \\
-\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ cut\ \-d\[aq]\ \[aq]\ \-f2\-)
-\ \ \ \ test\ \-n\ "${FILE}"
-\ \ \ \ rsync\ \-axqHAXWESR\ \-\-preallocate\ \-\-remove\-source\-files\ "${CACHE}/./${FILE}"\ "${BACKING}/"
+    FILE=$(find \[dq]${CACHE}\[dq] \-type f \-printf \[aq]%A\[at] %P\[rs]n\[aq] | \[rs]
+                  sort | \[rs]
+                  head \-n 1 | \[rs]
+                  cut \-d\[aq] \[aq] \-f2\-)
+    test \-n \[dq]${FILE}\[dq]
+    rsync \-axqHAXWESR \-\-preallocate \-\-remove\-source\-files \[dq]${CACHE}/./${FILE}\[dq] \[dq]${BACKING}/\[dq]
 done
-\f[]
+\f[R]
 .fi
 .SH PERFORMANCE
 .PP
@@ -1840,53 +1761,59 @@ performance is that of the underlying devices.
 However, given it is a FUSE filesystem working from userspace there is
 an increase in overhead relative to kernel based solutions.
 That said the performance can match the theoretical max but it depends
-greatly on the system\[aq]s configuration.
+greatly on the system\[cq]s configuration.
 Especially when adding network filesystems into the mix there are many
 variables which can impact performance.
 Drive speeds and latency, network speeds and latency, general
 concurrency, read/write sizes, etc.
 Unfortunately, given the number of variables it has been difficult to
 find a single set of settings which provide optimal performance.
-If you\[aq]re having performance issues please look over the suggestions
+If you\[cq]re having performance issues please look over the suggestions
 below (including the benchmarking section.)
 .PP
 NOTE: be sure to read about these features before changing them to
 understand what behaviors it may impact
 .IP \[bu] 2
-enable (or disable) \f[C]splice_move\f[], \f[C]splice_read\f[], and
-\f[C]splice_write\f[]
+enable (or disable) \f[C]splice_move\f[R], \f[C]splice_read\f[R], and
+\f[C]splice_write\f[R]
 .IP \[bu] 2
-disable \f[C]security_capability\f[] and/or \f[C]xattr\f[]
+disable \f[C]security_capability\f[R] and/or \f[C]xattr\f[R]
 .IP \[bu] 2
-increase cache timeouts \f[C]cache.attr\f[], \f[C]cache.entry\f[],
-\f[C]cache.negative_entry\f[]
+increase cache timeouts \f[C]cache.attr\f[R], \f[C]cache.entry\f[R],
+\f[C]cache.negative_entry\f[R]
 .IP \[bu] 2
-enable (or disable) page caching (\f[C]cache.files\f[])
+enable (or disable) page caching (\f[C]cache.files\f[R])
 .IP \[bu] 2
-enable \f[C]cache.writeback\f[]
+enable \f[C]cache.writeback\f[R]
 .IP \[bu] 2
-enable \f[C]cache.open\f[]
+enable \f[C]cache.open\f[R]
 .IP \[bu] 2
-enable \f[C]cache.statfs\f[]
+enable \f[C]cache.statfs\f[R]
 .IP \[bu] 2
-enable \f[C]cache.symlinks\f[]
+enable \f[C]cache.symlinks\f[R]
 .IP \[bu] 2
-enable \f[C]cache.readdir\f[]
+enable \f[C]cache.readdir\f[R]
 .IP \[bu] 2
 change the number of worker threads
 .IP \[bu] 2
-disable \f[C]posix_acl\f[]
+disable \f[C]posix_acl\f[R]
 .IP \[bu] 2
-disable \f[C]async_read\f[]
+disable \f[C]async_read\f[R]
 .IP \[bu] 2
-test theoretical performance using \f[C]nullrw\f[] or mounting a ram
+test theoretical performance using \f[C]nullrw\f[R] or mounting a ram
 disk
 .IP \[bu] 2
-use \f[C]symlinkify\f[] if your data is largely static and read\-only
+use \f[C]symlinkify\f[R] if your data is largely static and read\-only
 .IP \[bu] 2
 use tiered cache drives
 .IP \[bu] 2
 use LVM and LVM cache to place a SSD in front of your HDDs
+.IP \[bu] 2
+if \f[C]cache.files\f[R] is enabled increase readahead:
+\f[C]echo \[dq]1024\[dq] > /sys/class/bdi/0:$(stat \-c%d /MOUNT)/read_ahead_kb\f[R]
+.IP \[bu] 2
+increase readahead on all devices:
+\f[C]ls \-1 /sys/class/bdi/*/read_ahead_kb | xargs \-n1 \-I{} sh \-c \[dq]echo 1024 > {}\[dq]\f[R]
 .PP
 If you come across a setting that significantly impacts performance
 please contact trapexit so he may investigate further.
@@ -1896,40 +1823,40 @@ Filesystems are complicated.
 They do many things and many of those are interconnected.
 Additionally, the OS, drivers, hardware, etc.
 all can impact performance.
-Therefore, when benchmarking, it is \f[B]necessary\f[] that the test
+Therefore, when benchmarking, it is \f[B]necessary\f[R] that the test
 focus as narrowly as possible.
 .PP
 For most throughput is the key benchmark.
-To test throughput \f[C]dd\f[] is useful but \f[B]must\f[] be used with
-the correct settings in order to ensure the filesystem or device is
+To test throughput \f[C]dd\f[R] is useful but \f[B]must\f[R] be used
+with the correct settings in order to ensure the filesystem or device is
 actually being tested.
 The OS can and will cache data.
 Without forcing synchronous reads and writes and/or disabling caching
-the values returned will not be representative of the device\[aq]s true
+the values returned will not be representative of the device\[cq]s true
 performance.
 .PP
 When benchmarking through mergerfs ensure you only use 1 branch to
 remove any possibility of the policies complicating the situation.
 Benchmark the underlying filesystem first and then mount mergerfs over
 it and test again.
-If you\[aq]re experience speeds below your expectation you will need to
+If you\[cq]re experience speeds below your expectation you will need to
 narrow down precisely which component is leading to the slowdown.
 Preferably test the following in the order listed (but not combined).
 .IP "1." 3
-Enable \f[C]nullrw\f[] mode with \f[C]nullrw=true\f[].
+Enable \f[C]nullrw\f[R] mode with \f[C]nullrw=true\f[R].
 This will effectively make reads and writes no\-ops.
 Removing the underlying device / filesystem from the equation.
 This will give us the top theoretical speeds.
 .IP "2." 3
-Mount mergerfs over \f[C]tmpfs\f[].
-\f[C]tmpfs\f[] is a RAM disk.
+Mount mergerfs over \f[C]tmpfs\f[R].
+\f[C]tmpfs\f[R] is a RAM disk.
 Extremely high speed and very low latency.
 This is a more realistic best case scenario.
-Example: \f[C]mount\ \-t\ tmpfs\ \-o\ size=2G\ tmpfs\ /tmp/tmpfs\f[]
+Example: \f[C]mount \-t tmpfs \-o size=2G tmpfs /tmp/tmpfs\f[R]
 .IP "3." 3
 Mount mergerfs over a local drive.
 NVMe, SSD, HDD, etc.
-If you have more than one I\[aq]d suggest testing each of them as drives
+If you have more than one I\[cq]d suggest testing each of them as drives
 and/or controllers (their drivers) could impact performance.
 .IP "4." 3
 Finally, if you intend to use mergerfs with a network filesystem, either
@@ -1939,12 +1866,12 @@ each of those alone as above.
 Once you find the component which has the performance issue you can do
 further testing with different options to see if they impact
 performance.
-For reads and writes the most relevant would be: \f[C]cache.files\f[],
-\f[C]async_read\f[], \f[C]splice_move\f[], \f[C]splice_read\f[],
-\f[C]splice_write\f[].
+For reads and writes the most relevant would be: \f[C]cache.files\f[R],
+\f[C]async_read\f[R], \f[C]splice_move\f[R], \f[C]splice_read\f[R],
+\f[C]splice_write\f[R].
 Less likely but relevant when using NFS or with certain filesystems
-would be \f[C]security_capability\f[], \f[C]xattr\f[], and
-\f[C]posix_acl\f[].
+would be \f[C]security_capability\f[R], \f[C]xattr\f[R], and
+\f[C]posix_acl\f[R].
 If you find a specific system, drive, filesystem, controller, etc.
 that performs poorly contact trapexit so he may investigate further.
 .PP
@@ -1952,11 +1879,11 @@ Sometimes the problem is really the application accessing or writing
 data through mergerfs.
 Some software use small buffer sizes which can lead to more requests and
 therefore greater overhead.
-You can test this out yourself by replace \f[C]bs=1M\f[] in the examples
-below with \f[C]ibs\f[] or \f[C]obs\f[] and using a size of \f[C]512\f[]
-instead of \f[C]1M\f[].
-In one example test using \f[C]nullrw\f[] the write speed dropped from
-4.9GB/s to 69.7MB/s when moving from \f[C]1M\f[] to \f[C]512\f[].
+You can test this out yourself by replace \f[C]bs=1M\f[R] in the
+examples below with \f[C]ibs\f[R] or \f[C]obs\f[R] and using a size of
+\f[C]512\f[R] instead of \f[C]1M\f[R].
+In one example test using \f[C]nullrw\f[R] the write speed dropped from
+4.9GB/s to 69.7MB/s when moving from \f[C]1M\f[R] to \f[C]512\f[R].
 Similar results were had when testing reads.
 Small writes overhead may be improved by leveraging a write cache but in
 casual tests little gain was found.
@@ -1969,15 +1896,15 @@ Contact trapexit so he may investigate further.
 .IP
 .nf
 \f[C]
-$\ dd\ if=/dev/zero\ of=/mnt/mergerfs/1GB.file\ bs=1M\ count=1024\ oflag=nocache\ conv=fdatasync\ status=progress
-\f[]
+$ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=fdatasync status=progress
+\f[R]
 .fi
 .SS read benchmark
 .IP
 .nf
 \f[C]
-$\ dd\ if=/mnt/mergerfs/1GB.file\ of=/dev/null\ bs=1M\ count=1024\ iflag=nocache\ conv=fdatasync\ status=progress
-\f[]
+$ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress
+\f[R]
 .fi
 .SS other benchmarks
 .PP
@@ -1989,69 +1916,70 @@ benchmarks as well just in case.
 .nf
 \f[C]
 sync
-echo\ 3\ |\ sudo\ tee\ /proc/sys/vm/drop_caches
-\f[]
+echo 3 | sudo tee /proc/sys/vm/drop_caches
+\f[R]
 .fi
 .SH TIPS / NOTES
 .IP \[bu] 2
 This document is very literal and thorough.
 Unless there is a bug things work as described.
-If a suspected feature isn\[aq]t mentioned it doesn\[aq]t exist.
-If certain libfuse arguments aren\[aq]t listed they probably
-shouldn\[aq]t be used.
+If a suspected feature isn\[cq]t mentioned it doesn\[cq]t exist.
+If certain libfuse arguments aren\[cq]t listed they probably
+shouldn\[cq]t be used.
 .IP \[bu] 2
-Ensure you\[aq]re using the latest version.
+Ensure you\[cq]re using the latest version.
 Few distros have the latest version.
 .IP \[bu] 2
-\f[B]use_ino\f[] will only work when used with mergerfs 2.18.0 and
+\f[B]use_ino\f[R] will only work when used with mergerfs 2.18.0 and
 above.
 .IP \[bu] 2
-Run mergerfs as \f[C]root\f[] (with \f[B]allow_other\f[]) unless
-you\[aq]re merging paths which are owned exclusively and fully by the
+Run mergerfs as \f[C]root\f[R] (with \f[B]allow_other\f[R]) unless
+you\[cq]re merging paths which are owned exclusively and fully by the
 same user otherwise strange permission issues may arise.
-mergerfs is designed and intended to be run as \f[C]root\f[].
+mergerfs is designed and intended to be run as \f[C]root\f[R].
 .IP \[bu] 2
-If you don\[aq]t see some directories and files you expect, policies
+If you don\[cq]t see some directories and files you expect, policies
 seem to skip branches, you get strange permission errors, etc.
-be sure the underlying filesystems\[aq] permissions are all the same.
-Use \f[C]mergerfs.fsck\f[] to audit the drive for out of sync
+be sure the underlying filesystems\[cq] permissions are all the same.
+Use \f[C]mergerfs.fsck\f[R] to audit the drive for out of sync
 permissions.
 .IP \[bu] 2
 If you still have permission issues be sure you are using POSIX ACL
 compliant filesystems.
-mergerfs doesn\[aq]t generally make exceptions for FAT, NTFS, or other
+mergerfs doesn\[cq]t generally make exceptions for FAT, NTFS, or other
 non\-POSIX filesystem.
 .IP \[bu] 2
-Do \f[B]not\f[] use \f[C]cache.files=off\f[] if you expect applications
-(such as rtorrent) to use mmap (http://linux.die.net/man/2/mmap) files.
+Do \f[B]not\f[R] use \f[C]cache.files=off\f[R] if you expect
+applications (such as rtorrent) to use
+mmap (http://linux.die.net/man/2/mmap) files.
 Shared mmap is not currently supported in FUSE w/ page caching disabled.
-Enabling \f[C]dropcacheonclose\f[] is recommended when
-\f[C]cache.files=partial|full|auto\-full\f[].
+Enabling \f[C]dropcacheonclose\f[R] is recommended when
+\f[C]cache.files=partial|full|auto\-full\f[R].
 .IP \[bu] 2
 Kodi (http://kodi.tv), Plex (http://plex.tv),
 Subsonic (http://subsonic.org), etc.
 can use directory mtime (http://linux.die.net/man/2/stat) to more
 efficiently determine whether to scan for new content rather than simply
 performing a full scan.
-If using the default \f[B]getattr\f[] policy of \f[B]ff\f[] it\[aq]s
+If using the default \f[B]getattr\f[R] policy of \f[B]ff\f[R] it\[cq]s
 possible those programs will miss an update on account of it returning
-the first directory found\[aq]s \f[B]stat\f[] info and its a later
-directory on another mount which had the \f[B]mtime\f[] recently
+the first directory found\[cq]s \f[B]stat\f[R] info and it\[cq]s a later
+directory on another mount which had the \f[B]mtime\f[R] recently
 updated.
-To fix this you will want to set \f[B]func.getattr=newest\f[].
-Remember though that this is just \f[B]stat\f[].
-If the file is later \f[B]open\f[]\[aq]ed or \f[B]unlink\f[]\[aq]ed and
-the policy is different for those then a completely different file or
-directory could be acted on.
+To fix this you will want to set \f[B]func.getattr=newest\f[R].
+Remember though that this is just \f[B]stat\f[R].
+If the file is later \f[B]open\f[R]\[cq]ed or \f[B]unlink\f[R]\[cq]ed
+and the policy is different for those then a completely different file
+or directory could be acted on.
 .IP \[bu] 2
 Some policies mixed with some functions may result in strange behaviors.
-Not that some of these behaviors and race conditions couldn\[aq]t happen
-outside \f[B]mergerfs\f[] but that they are far more likely to occur on
+Not that some of these behaviors and race conditions couldn\[cq]t happen
+outside \f[B]mergerfs\f[R] but that they are far more likely to occur on
 account of the attempt to merge together multiple sources of data which
 could be out of sync due to the different policies.
 .IP \[bu] 2
-For consistency its generally best to set \f[B]category\f[] wide
-policies rather than individual \f[B]func\f[]\[aq]s.
+For consistency its generally best to set \f[B]category\f[R] wide
+policies rather than individual \f[B]func\f[R]\[cq]s.
 This will help limit the confusion of tools such as
 rsync (http://linux.die.net/man/1/rsync).
 However, the flexibility is there if needed.
@@ -2061,9 +1989,9 @@ However, the flexibility is there if needed.
 <https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs>
 .SS directory mtime is not being updated
 .PP
-Remember that the default policy for \f[C]getattr\f[] is \f[C]ff\f[].
+Remember that the default policy for \f[C]getattr\f[R] is \f[C]ff\f[R].
 The information for the first directory found will be returned.
-If it wasn\[aq]t the directory which had been updated then it will
+If it wasn\[cq]t the directory which had been updated then it will
 appear outdated.
 .PP
 The reason this is the default is because any other policy would be more
@@ -2072,26 +2000,26 @@ To always return the directory with the most recent mtime or a faked
 value based on all found would require a scan of all drives.
 .PP
 If you always want the directory information from the one with the most
-recent mtime then use the \f[C]newest\f[] policy for \f[C]getattr\f[].
-.SS \[aq]mv /mnt/pool/foo /mnt/disk1/foo\[aq] removes \[aq]foo\[aq]
+recent mtime then use the \f[C]newest\f[R] policy for \f[C]getattr\f[R].
+.SS `mv /mnt/pool/foo /mnt/disk1/foo' removes `foo'
 .PP
 This is not a bug.
 .PP
-Run in verbose mode to better understand what\[aq]s happening:
+Run in verbose mode to better understand what\[cq]s happening:
 .IP
 .nf
 \f[C]
-$\ mv\ \-v\ /mnt/pool/foo\ /mnt/disk1/foo
-copied\ \[aq]/mnt/pool/foo\[aq]\ \->\ \[aq]/mnt/disk1/foo\[aq]
-removed\ \[aq]/mnt/pool/foo\[aq]
-$\ ls\ /mnt/pool/foo
-ls:\ cannot\ access\ \[aq]/mnt/pool/foo\[aq]:\ No\ such\ file\ or\ directory
-\f[]
+$ mv \-v /mnt/pool/foo /mnt/disk1/foo
+copied \[aq]/mnt/pool/foo\[aq] \-> \[aq]/mnt/disk1/foo\[aq]
+removed \[aq]/mnt/pool/foo\[aq]
+$ ls /mnt/pool/foo
+ls: cannot access \[aq]/mnt/pool/foo\[aq]: No such file or directory
+\f[R]
 .fi
 .PP
-\f[C]mv\f[], when working across devices, is copying the source to
+\f[C]mv\f[R], when working across devices, is copying the source to
 target and then removing the source.
-Since the source \f[B]is\f[] the target in this case, depending on the
+Since the source \f[B]is\f[R] the target in this case, depending on the
 unlink policy, it will remove the just copied file and other files
 across the branches.
 .PP
@@ -2100,7 +2028,7 @@ mergerfs.dedup to clean up the old paths or manually remove them from
 the branches directly.
 .SS cached memory appears greater than it should be
 .PP
-Use \f[C]cache.files=off\f[] and/or \f[C]dropcacheonclose=true\f[].
+Use \f[C]cache.files=off\f[R] and/or \f[C]dropcacheonclose=true\f[R].
 See the section on page caching.
 .SS NFS clients returning ESTALE / Stale file handle
 .PP
@@ -2116,113 +2044,114 @@ use_ino
 inodecalc=path\-hash
 .SS rtorrent fails with ENODEV (No such device)
 .PP
-Be sure to set \f[C]cache.files=partial|full|auto\-full\f[] or turn off
-\f[C]direct_io\f[].
+Be sure to set \f[C]cache.files=partial|full|auto\-full\f[R] or turn off
+\f[C]direct_io\f[R].
 rtorrent and some other applications use
 mmap (http://linux.die.net/man/2/mmap) to read and write to files and
 offer no fallback to traditional methods.
-FUSE does not currently support mmap while using \f[C]direct_io\f[].
-There may be a performance penalty on writes with \f[C]direct_io\f[] off
-as well as the problem of double caching but it\[aq]s the only way to
-get such applications to work.
+FUSE does not currently support mmap while using \f[C]direct_io\f[R].
+There may be a performance penalty on writes with \f[C]direct_io\f[R]
+off as well as the problem of double caching but it\[cq]s the only way
+to get such applications to work.
 If the performance loss is too high for other apps you can mount
 mergerfs twice.
-Once with \f[C]direct_io\f[] enabled and one without it.
-Be sure to set \f[C]dropcacheonclose=true\f[] if not using
-\f[C]direct_io\f[].
-.SS Plex doesn\[aq]t work with mergerfs
+Once with \f[C]direct_io\f[R] enabled and one without it.
+Be sure to set \f[C]dropcacheonclose=true\f[R] if not using
+\f[C]direct_io\f[R].
+.SS Plex doesn\[cq]t work with mergerfs
 .PP
 It does.
-If you\[aq]re trying to put Plex\[aq]s config / metadata / database on
-mergerfs you can\[aq]t set \f[C]cache.files=off\f[] because Plex is
+If you\[cq]re trying to put Plex\[cq]s config / metadata / database on
+mergerfs you can\[cq]t set \f[C]cache.files=off\f[R] because Plex is
 using sqlite3 with mmap enabled.
-Shared mmap is not supported by Linux\[aq]s FUSE implementation when
+Shared mmap is not supported by Linux\[cq]s FUSE implementation when
 page caching is disabled.
 To fix this place the data elsewhere (preferable) or enable
-\f[C]cache.files\f[] (with \f[C]dropcacheonclose=true\f[]).
+\f[C]cache.files\f[R] (with \f[C]dropcacheonclose=true\f[R]).
 Sqlite3 does not need mmap but the developer needs to fall back to
 standard IO if mmap fails.
 .PP
-If the issue is that scanning doesn\[aq]t seem to pick up media then be
-sure to set \f[C]func.getattr=newest\f[] though generally a full scan
+If the issue is that scanning doesn\[cq]t seem to pick up media then be
+sure to set \f[C]func.getattr=newest\f[R] though generally a full scan
 will pick up all media anyway.
 .SS When a program tries to move or rename a file it fails
 .PP
-Please read the section above regarding rename & link (#rename--link).
+Please read the section above regarding rename & link.
 .PP
 The problem is that many applications do not properly handle
-\f[C]EXDEV\f[] errors which \f[C]rename\f[] and \f[C]link\f[] may return
-even though they are perfectly valid situations which do not indicate
-actual drive or OS errors.
+\f[C]EXDEV\f[R] errors which \f[C]rename\f[R] and \f[C]link\f[R] may
+return even though they are perfectly valid situations which do not
+indicate actual drive or OS errors.
 The error will only be returned by mergerfs if using a path preserving
 policy as described in the policy section above.
 If you do not care about path preservation simply change the mergerfs
 policy to the non\-path preserving version.
-For example: \f[C]\-o\ category.create=mfs\f[]
+For example: \f[C]\-o category.create=mfs\f[R]
 .PP
 Ideally the offending software would be fixed and it is recommended that
-if you run into this problem you contact the software\[aq]s author and
-request proper handling of \f[C]EXDEV\f[] errors.
+if you run into this problem you contact the software\[cq]s author and
+request proper handling of \f[C]EXDEV\f[R] errors.
 .SS my 32bit software has problems
 .PP
 Some software have problems with 64bit inode values.
 The symptoms can include EOVERFLOW errors when trying to list files.
-You can address this by setting \f[C]inodecalc\f[] to one of the 32bit
+You can address this by setting \f[C]inodecalc\f[R] to one of the 32bit
 based algos as described in the relevant section.
 .SS Samba: Moving files / directories fails
 .PP
 Workaround: Copy the file/directory and then remove the original rather
 than move.
 .PP
-This isn\[aq]t an issue with Samba but some SMB clients.
+This isn\[cq]t an issue with Samba but some SMB clients.
 GVFS\-fuse v1.20.3 and prior (found in Ubuntu 14.04 among others) failed
 to handle certain error codes correctly.
-Particularly \f[B]STATUS_NOT_SAME_DEVICE\f[] which comes from the
-\f[B]EXDEV\f[] which is returned by \f[B]rename\f[] when the call is
+Particularly \f[B]STATUS_NOT_SAME_DEVICE\f[R] which comes from the
+\f[B]EXDEV\f[R] which is returned by \f[B]rename\f[R] when the call is
 crossing mount points.
-When a program gets an \f[B]EXDEV\f[] it needs to explicitly take an
+When a program gets an \f[B]EXDEV\f[R] it needs to explicitly take an
 alternate action to accomplish its goal.
-In the case of \f[B]mv\f[] or similar it tries \f[B]rename\f[] and on
-\f[B]EXDEV\f[] falls back to a manual copying of data between the two
+In the case of \f[B]mv\f[R] or similar it tries \f[B]rename\f[R] and on
+\f[B]EXDEV\f[R] falls back to a manual copying of data between the two
 locations and unlinking the source.
-In these older versions of GVFS\-fuse if it received \f[B]EXDEV\f[] it
-would translate that into \f[B]EIO\f[].
-This would cause \f[B]mv\f[] or most any application attempting to move
+In these older versions of GVFS\-fuse if it received \f[B]EXDEV\f[R] it
+would translate that into \f[B]EIO\f[R].
+This would cause \f[B]mv\f[R] or most any application attempting to move
 files around on that SMB share to fail with a IO error.
 .PP
 GVFS\-fuse v1.22.0 (https://bugzilla.gnome.org/show_bug.cgi?id=734568)
 and above fixed this issue but a large number of systems use the older
 release.
 On Ubuntu the version can be checked by issuing
-\f[C]apt\-cache\ showpkg\ gvfs\-fuse\f[].
+\f[C]apt\-cache showpkg gvfs\-fuse\f[R].
 Most distros released in 2015 seem to have the updated release and will
 work fine but older systems may not.
 Upgrading gvfs\-fuse or the distro in general will address the problem.
 .PP
-In Apple\[aq]s MacOSX 10.9 they replaced Samba (client and server) with
+In Apple\[cq]s MacOSX 10.9 they replaced Samba (client and server) with
 their own product.
-It appears their new client does not handle \f[B]EXDEV\f[] either and
+It appears their new client does not handle \f[B]EXDEV\f[R] either and
 responds similar to older release of gvfs on Linux.
 .SS Trashing files occasionally fails
 .PP
 This is the same issue as with Samba.
-\f[C]rename\f[] returns \f[C]EXDEV\f[] (in our case that will really
-only happen with path preserving policies like \f[C]epmfs\f[]) and the
-software doesn\[aq]t handle the situation well.
+\f[C]rename\f[R] returns \f[C]EXDEV\f[R] (in our case that will really
+only happen with path preserving policies like \f[C]epmfs\f[R]) and the
+software doesn\[cq]t handle the situation well.
 This is unfortunately a common failure of software which moves files
 around.
-The standard indicates that an implementation \f[C]MAY\f[] choose to
+The standard indicates that an implementation \f[C]MAY\f[R] choose to
 support non\-user home directory trashing of files (which is a
-\f[C]MUST\f[]).
-The implementation \f[C]MAY\f[] also support "top directory trashes"
-which many probably do.
-.PP
-To create a \f[C]$topdir/.Trash\f[] directory as defined in the standard
-use the mergerfs\-tools (https://github.com/trapexit/mergerfs-tools)
-tool \f[C]mergerfs.mktrash\f[].
+\f[C]MUST\f[R]).
+The implementation \f[C]MAY\f[R] also support \[lq]top directory
+trashes\[rq] which many probably do.
+.PP
+To create a \f[C]$topdir/.Trash\f[R] directory as defined in the
+standard use the
+mergerfs\-tools (https://github.com/trapexit/mergerfs-tools) tool
+\f[C]mergerfs.mktrash\f[R].
 .SS tar: Directory renamed before its status could be extracted
 .PP
-Make sure to use the \f[C]use_ino\f[] option.
+Make sure to use the \f[C]use_ino\f[R] option.
 .SS Supplemental user groups
 .PP
 Due to the overhead of
@@ -2234,14 +2163,14 @@ particular thread needs to change credentials and will keep that data
 for the lifetime of the thread.
 This means that if a user is added to a group it may not be picked up
 without the restart of mergerfs.
-However, since the high level FUSE API\[aq]s (at least the standard
-version) thread pool dynamically grows and shrinks it\[aq]s possible
+However, since the high level FUSE API\[cq]s (at least the standard
+version) thread pool dynamically grows and shrinks it\[cq]s possible
 that over time a thread will be killed and later a new thread with no
 cache will start and query the new data.
 .PP
 The gid cache uses fixed storage to simplify the design and be
 compatible with older systems which may not have C++11 compilers.
-There is enough storage for 256 users\[aq] supplemental groups.
+There is enough storage for 256 users\[cq] supplemental groups.
 Each user is allowed up to 32 supplemental groups.
 Linux >= 2.6.3 allows up to 65535 groups per user but most other *nixs
 allow far less.
@@ -2250,18 +2179,18 @@ The system does handle overflow gracefully.
 If the user has more than 32 supplemental groups only the first 32 will
 be used.
 If more than 256 users are using the system when an uncached user is
-found it will evict an existing user\[aq]s cache at random.
-So long as there aren\[aq]t more than 256 active users this should be
+found it will evict an existing user\[cq]s cache at random.
+So long as there aren\[cq]t more than 256 active users this should be
 fine.
 If either value is too low for your needs you will have to modify
-\f[C]gidcache.hpp\f[] to increase the values.
+\f[C]gidcache.hpp\f[R] to increase the values.
 Note that doing so will increase the memory needed by each thread.
 .PP
 While not a bug some users have found when using containers that
-supplemental groups defined inside the container don\[aq]t work properly
+supplemental groups defined inside the container don\[cq]t work properly
 with regard to permissions.
 This is expected as mergerfs lives outside the container and therefore
-is querying the host\[aq]s group database.
+is querying the host\[cq]s group database.
 There might be a hack to work around this (make mergerfs read the
 /etc/group file in the container) but it is not yet implemented and
 would be limited to Linux and the /etc/group DB.
@@ -2269,16 +2198,15 @@ Preferably users would mount in the host group file into the containers
 or use a standard shared user & groups technology like NIS or LDAP.
 .SS mergerfs or libfuse crashing
 .PP
-First...
-always upgrade to the latest version unless told otherwise.
+First\&... always upgrade to the latest version unless told otherwise.
 .PP
 If using mergerfs below 2.22.0:
 .PP
 If suddenly the mergerfs mount point disappears and
-\f[C]Transport\ endpoint\ is\ not\ connected\f[] is returned when
-attempting to perform actions within the mount directory \f[B]and\f[]
-the version of libfuse (use \f[C]mergerfs\ \-v\f[] to find the version)
-is older than \f[C]2.9.4\f[] its likely due to a bug in libfuse.
+\f[C]Transport endpoint is not connected\f[R] is returned when
+attempting to perform actions within the mount directory \f[B]and\f[R]
+the version of libfuse (use \f[C]mergerfs \-v\f[R] to find the version)
+is older than \f[C]2.9.4\f[R] its likely due to a bug in libfuse.
 Affected versions of libfuse can be found in Debian Wheezy, Ubuntu
 Precise and others.
 .PP
@@ -2295,10 +2223,10 @@ First upgrade if possible, check the known bugs section, and contact
 trapexit.
 .SS mergerfs appears to be crashing or exiting
 .PP
-There seems to be an issue with Linux version \f[C]4.9.0\f[] and above
+There seems to be an issue with Linux version \f[C]4.9.0\f[R] and above
 in which an invalid message appears to be transmitted to libfuse (used
 by mergerfs) causing it to exit.
-No messages will be printed in any logs as it\[aq]s not a proper crash.
+No messages will be printed in any logs as it\[cq]s not a proper crash.
 Debugging of the issue is still ongoing and can be followed via the
 fuse\-devel
 thread (https://sourceforge.net/p/fuse/mailman/message/35662577).
@@ -2308,64 +2236,63 @@ Please update.
 This is only happened to mergerfs versions at or below v2.25.x and will
 not occur in more recent versions.
 .SH FAQ
-.SS How well does mergerfs scale? Is it "production ready?"
+.SS How well does mergerfs scale? Is it \[lq]production ready?\[rq]
 .PP
 Users have reported running mergerfs on everything from a Raspberry Pi
 to dual socket Xeon systems with >20 cores.
-I\[aq]m aware of at least a few companies which use mergerfs in
+I\[cq]m aware of at least a few companies which use mergerfs in
 production.
 Open Media Vault (https://www.openmediavault.org) includes mergerfs as
 its sole solution for pooling drives.
 The author of mergerfs had it running for over 300 days managing 16+
 drives with reasonably heavy 24/7 read and write usage.
-Stopping only after the machine\[aq]s power supply died.
+Stopping only after the machine\[cq]s power supply died.
 .PP
 Most serious issues (crashes or data corruption) have been due to kernel
 bugs (https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs).
 All of which are fixed in stable releases.
-.SS Can mergerfs be used with drives which already have data / are in
-use?
+.SS Can mergerfs be used with drives which already have data / are in use?
 .PP
 Yes.
-MergerFS is a proxy and does \f[B]NOT\f[] interfere with the normal form
-or function of the drives / mounts / paths it manages.
-.PP
-MergerFS is \f[B]not\f[] a traditional filesystem.
-MergerFS is \f[B]not\f[] RAID.
-It does \f[B]not\f[] manipulate the data that passes through it.
-It does \f[B]not\f[] shard data across drives.
-It merely shards some \f[B]behavior\f[] and aggregates others.
+MergerFS is a proxy and does \f[B]NOT\f[R] interfere with the normal
+form or function of the drives / mounts / paths it manages.
+.PP
+MergerFS is \f[B]not\f[R] a traditional filesystem.
+MergerFS is \f[B]not\f[R] RAID.
+It does \f[B]not\f[R] manipulate the data that passes through it.
+It does \f[B]not\f[R] shard data across drives.
+It merely shards some \f[B]behavior\f[R] and aggregates others.
 .SS Can mergerfs be removed without affecting the data?
 .PP
-See the previous question\[aq]s answer.
+See the previous question\[cq]s answer.
 .SS What policies should I use?
 .PP
-Unless you\[aq]re doing something more niche the average user is
-probably best off using \f[C]mfs\f[] for \f[C]category.create\f[].
+Unless you\[cq]re doing something more niche the average user is
+probably best off using \f[C]mfs\f[R] for \f[C]category.create\f[R].
 It will spread files out across your branches based on available space.
-Use \f[C]mspmfs\f[] if you want to try to colocate the data a bit more.
-You may want to use \f[C]lus\f[] if you prefer a slightly different
+Use \f[C]mspmfs\f[R] if you want to try to colocate the data a bit more.
+You may want to use \f[C]lus\f[R] if you prefer a slightly different
 distribution of data if you have a mix of smaller and larger drives.
-Generally though \f[C]mfs\f[], \f[C]lus\f[], or even \f[C]rand\f[] are
-good for the general use case.
+Generally though \f[C]mfs\f[R], \f[C]lus\f[R], or even \f[C]rand\f[R]
+are good for the general use case.
 If you are starting with an imbalanced pool you can use the tool
-\f[B]mergerfs.balance\f[] to redistribute files across the pool.
+\f[B]mergerfs.balance\f[R] to redistribute files across the pool.
 .PP
 If you really wish to try to colocate files based on directory you can
-set \f[C]func.create\f[] to \f[C]epmfs\f[] or similar and
-\f[C]func.mkdir\f[] to \f[C]rand\f[] or \f[C]eprand\f[] depending on if
-you just want to colocate generally or on specific branches.
-Either way the \f[I]need\f[] to colocate is rare.
+set \f[C]func.create\f[R] to \f[C]epmfs\f[R] or similar and
+\f[C]func.mkdir\f[R] to \f[C]rand\f[R] or \f[C]eprand\f[R] depending on
+if you just want to colocate generally or on specific branches.
+Either way the \f[I]need\f[R] to colocate is rare.
 For instance: if you wish to remove the drive regularly and want the
-data to predictably be on that drive or if you don\[aq]t use backup at
-all and don\[aq]t wish to replace that data piecemeal.
+data to predictably be on that drive or if you don\[cq]t use backup at
+all and don\[cq]t wish to replace that data piecemeal.
 In which case using path preservation can help but will require some
 manual attention.
 Colocating after the fact can be accomplished using the
-\f[B]mergerfs.consolidate\f[] tool.
-If you don\[aq]t need strict colocation which the \f[C]ep\f[] policies
-provide then you can use the \f[C]msp\f[] based policies which will walk
-back the path till finding a branch that works.
+\f[B]mergerfs.consolidate\f[R] tool.
+If you don\[cq]t need strict colocation which the \f[C]ep\f[R] policies
+provide then you can use the \f[C]msp\f[R] based policies which will
+walk back the path till finding a branch that works.
 .PP
 Ultimately there is no correct answer.
 It is a preference or based on some particular need.
@@ -2373,34 +2300,34 @@ mergerfs is very easy to test and experiment with.
 I suggest creating a test setup and experimenting to get a sense of what
 you want.
 .PP
-The reason \f[C]mfs\f[] is not the default \f[C]category.create\f[]
+The reason \f[C]mfs\f[R] is not the default \f[C]category.create\f[R]
 policy is historical.
 When/if a 3.X gets released it will be changed to minimize confusion
 people often have with path preserving policies.
 .SS What settings should I use?
 .PP
 Depends on what features you want.
-Generally speaking there are no "wrong" settings.
+Generally speaking there are no \[lq]wrong\[rq] settings.
 All settings are performance or feature related.
 The best bet is to read over the available options and choose what fits
 your situation.
-If something isn\[aq]t clear from the documentation please reach out and
+If something isn\[cq]t clear from the documentation please reach out and
 the documentation will be improved.
 .PP
 That said, for the average person, the following should be fine:
 .PP
-\f[C]\-o\ use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs\f[]
+\f[C]\-o use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs\f[R]
 .SS Why are all my files ending up on 1 drive?!
 .PP
 Did you start with empty drives?
-Did you explicitly configure a \f[C]category.create\f[] policy?
-Are you using an \f[C]existing\ path\f[] / \f[C]path\ preserving\f[]
+Did you explicitly configure a \f[C]category.create\f[R] policy?
+Are you using an \f[C]existing path\f[R] / \f[C]path preserving\f[R]
 policy?
 .PP
-The default create policy is \f[C]epmfs\f[].
+The default create policy is \f[C]epmfs\f[R].
 That is a path preserving algorithm.
-With such a policy for \f[C]mkdir\f[] and \f[C]create\f[] with a set of
-empty drives it will select only 1 drive when the first directory is
+With such a policy for \f[C]mkdir\f[R] and \f[C]create\f[R] with a set
+of empty drives it will select only 1 drive when the first directory is
 created.
 Anything, files or directories, created in that first directory will be
 placed on the same branch because it is preserving paths.
@@ -2408,24 +2335,24 @@ placed on the same branch because it is preserving paths.
 This catches a lot of new users off guard but changing the default would
 break the setup for many existing users.
 If you do not care about path preservation and wish your files to be
-spread across all your drives change to \f[C]mfs\f[] or similar policy
+spread across all your drives change to \f[C]mfs\f[R] or similar policy
 as described above.
-If you do want path preservation you\[aq]ll need to perform the manual
+If you do want path preservation you\[cq]ll need to perform the manual
 act of creating paths on the drives you want the data to land on before
 transferring your data.
-Setting \f[C]func.mkdir=epall\f[] can simplify managing path
-preservation for \f[C]create\f[].
-Or use \f[C]func.mkdir=rand\f[] if you\[aq]re interested in just
+Setting \f[C]func.mkdir=epall\f[R] can simplify managing path
+preservation for \f[C]create\f[R].
+Or use \f[C]func.mkdir=rand\f[R] if you\[cq]re interested in just
 grouping together directory content by drive.
 .SS Do hardlinks work?
 .PP
 Yes.
-You need to use \f[C]use_ino\f[] to support proper reporting of inodes
+You need to use \f[C]use_ino\f[R] to support proper reporting of inodes
 but they work regardless.
-See also the option \f[C]inodecalc\f[].
+See also the option \f[C]inodecalc\f[R].
 .PP
 What mergerfs does not do is fake hard links across branches.
-Read the section "rename & link" for how it works.
+Read the section \[lq]rename & link\[rq] for how it works.
 .PP
 Remember that hardlinks will NOT work across devices.
 That includes between the original filesystem and a mergerfs pool,
@@ -2440,31 +2367,30 @@ includes all the paths you need if you want links to work.
 .SS Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
 .PP
 Yes.
-They are completely unreleated pieces of software.
+They are completely unrelated pieces of software.
 .SS Can mergerfs run via Docker, Podman, Kubernetes, etc.
 .PP
 Yes.
-With Docker you\[aq]ll need to include
-\f[C]\-\-cap\-add=SYS_ADMIN\ \-\-device=/dev/fuse\ \-\-security\-opt=apparmor:unconfined\f[]
+With Docker you\[cq]ll need to include
+\f[C]\-\-cap\-add=SYS_ADMIN \-\-device=/dev/fuse \-\-security\-opt=apparmor:unconfined\f[R]
 or similar with other container runtimes.
 You should also be running it as root or given sufficient caps to change
 user and group identity as well as have root like filesystem
 permissions.
 .PP
-Keep in mind that you \f[B]MUST\f[] consider identity when using
+Keep in mind that you \f[B]MUST\f[R] consider identity when using
 containers.
 For example: supplemental groups will be picked up from the container
 unless you properly manage users and groups by sharing relevant /etc
 files or by using some other means to share identity across containers.
-Similarly if you use "rootless" containers and user namespaces to do
-uid/gid translations you \f[B]MUST\f[] consider that while managing
-shared files.
+Similarly if you use \[lq]rootless\[rq] containers and user namespaces
+to do uid/gid translations you \f[B]MUST\f[R] consider that while
+managing shared files.
 .PP
 Also, as mentioned by hotio (https://hotio.dev/containers/mergerfs),
 with Docker you should probably be mounting with
-\f[C]bind\-propagation\f[] set to \f[C]slave\f[].
-.SS Does mergerfs support CoW / copy\-on\-write / writes to read\-only
-filesystems?
+\f[C]bind\-propagation\f[R] set to \f[C]slave\f[R].
+.SS Does mergerfs support CoW / copy\-on\-write / writes to read\-only filesystems?
 .PP
 Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs
 or aufs sense.
@@ -2478,9 +2404,9 @@ file.
 If you want to write to a read\-only filesystem you should look at
 overlayfs.
 You can always include the overlayfs mount into a mergerfs pool.
-.SS Why can\[aq]t I see my files / directories?
+.SS Why can\[cq]t I see my files / directories?
 .PP
-It\[aq]s almost always a permissions issue.
+It\[cq]s almost always a permissions issue.
 Unlike mhddfs and unionfs\-fuse, which runs as root and attempts to
 access content as such, mergerfs always changes its credentials to that
 of the caller.
@@ -2494,12 +2420,12 @@ Whenever you run into a split permission issue (seeing some but not all
 files) try using
 mergerfs.fsck (https://github.com/trapexit/mergerfs-tools) tool to check
 for and fix the mismatch.
-If you aren\[aq]t seeing anything at all be sure that the basic
+If you aren\[cq]t seeing anything at all be sure that the basic
 permissions are correct.
 The user and group values are correct and that directories have their
 executable bit set.
-A common mistake by users new to Linux is to \f[C]chmod\ \-R\ 644\f[]
-when they should have \f[C]chmod\ \-R\ u=rwX,go=rX\f[].
+A common mistake by users new to Linux is to \f[C]chmod \-R 644\f[R]
+when they should have \f[C]chmod \-R u=rwX,go=rX\f[R].
 .PP
 If using a network filesystem such as NFS, SMB, CIFS (Samba) be sure to
 pay close attention to anything regarding permissioning and users.
@@ -2507,28 +2433,63 @@ Root squashing and user translation for instance has bitten a few
 mergerfs users.
 Some of these also affect the use of mergerfs from container platforms
 such as Docker.
-.SS Is my OS\[aq]s libfuse needed for mergerfs to work?
-.PP
-No.
-Normally \f[C]mount.fuse\f[] is needed to get mergerfs (or any FUSE
-filesystem to mount using the \f[C]mount\f[] command but in vendoring
-the libfuse library the \f[C]mount.fuse\f[] app has been renamed to
-\f[C]mount.mergerfs\f[] meaning the filesystem type in \f[C]fstab\f[]
-can simply be \f[C]mergerfs\f[].
+.SS Why use FUSE? Why not a kernel based solution?
+.PP
+As with any two solutions to a problem there are advantages and
+disadvantages to each one.
+.PP
+A FUSE based solution has all the downsides of FUSE:
+.IP \[bu] 2
+Higher IO latency due to the trips in and out of kernel space
+.IP \[bu] 2
+Higher general overhead due to trips in and out of kernel space
+.IP \[bu] 2
+Double caching when using page caching
+.IP \[bu] 2
+Misc limitations due to FUSE\[cq]s design
+.PP
+But FUSE also has a lot of upsides:
+.IP \[bu] 2
+Easier to offer a cross platform solution
+.IP \[bu] 2
+Easier forward and backward compatibility
+.IP \[bu] 2
+Easier updates for users
+.IP \[bu] 2
+Easier and faster release cadence
+.IP \[bu] 2
+Allows more flexibility in design and features
+.IP \[bu] 2
+Overall easier to write, secure, and maintain
+.IP \[bu] 2
+Ability to run without root access or need to change the kernel
+.IP \[bu] 2
+Much lower barrier to entry (getting code into the kernel takes a lot of
+time and effort initially)
+.PP
+FUSE was chosen because of all the advantages listed above.
+The negatives of FUSE do not outweigh the positives.
+.SS Is my OS\[cq]s libfuse needed for mergerfs to work?
+.PP
+No.\ Normally \f[C]mount.fuse\f[R] is needed to get mergerfs (or any
+FUSE filesystem to mount using the \f[C]mount\f[R] command but in
+vendoring the libfuse library the \f[C]mount.fuse\f[R] app has been
+renamed to \f[C]mount.mergerfs\f[R] meaning the filesystem type in
+\f[C]fstab\f[R] can simply be \f[C]mergerfs\f[R].
 That said there should be no harm in having it installed and continuing
-to using \f[C]fuse.mergerfs\f[] as the type in \f[C]/etc/fstab\f[].
+to using \f[C]fuse.mergerfs\f[R] as the type in \f[C]/etc/fstab\f[R].
 .PP
-If \f[C]mergerfs\f[] doesn\[aq]t work as a type it could be due to how
-the \f[C]mount.mergerfs\f[] tool was installed.
-Must be in \f[C]/sbin/\f[] with proper permissions.
+If \f[C]mergerfs\f[R] doesn\[cq]t work as a type it could be due to how
+the \f[C]mount.mergerfs\f[R] tool was installed.
+Must be in \f[C]/sbin/\f[R] with proper permissions.
 .SS Why was libfuse embedded into mergerfs?
 .IP "1." 3
 A significant number of users use mergerfs on distros with old versions
 of libfuse which have serious bugs.
-Requiring updated versions of libfuse on those distros isn\[aq]t
+Requiring updated versions of libfuse on those distros isn\[cq]t
 practical (no package offered, user inexperience, etc.).
 The only practical way to provide a stable runtime on those systems was
-to "vendor" / embed the library into the project.
+to \[lq]vendor\[rq] / embed the library into the project.
 .IP "2." 3
 mergerfs was written to use the high level API.
 There are a number of limitations in the HLAPI that make certain
@@ -2545,21 +2506,21 @@ Longer term the plan is to rewrite mergerfs to use the low level API.
 .PP
 See above first.
 .PP
-If/when mergerfs is rewritten to use the low\-level API then it\[aq]ll
-be plausible to support system libfuse but till then it\[aq]s simply too
+If/when mergerfs is rewritten to use the low\-level API then it\[cq]ll
+be plausible to support system libfuse but till then it\[cq]s simply too
 much work to manage the differences across the versions.
 .SS Why use mergerfs over mhddfs?
 .PP
 mhddfs is no longer maintained and has some known stability and security
 issues (see below).
-MergerFS provides a superset of mhddfs\[aq] features and should offer
+MergerFS provides a superset of mhddfs\[cq] features and should offer
 the same or maybe better performance.
 .PP
 Below is an example of mhddfs and mergerfs setup to work similarly.
 .PP
-\f[C]mhddfs\ \-o\ mlimit=4G,allow_other\ /mnt/drive1,/mnt/drive2\ /mnt/pool\f[]
+\f[C]mhddfs \-o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool\f[R]
 .PP
-\f[C]mergerfs\ \-o\ minfreespace=4G,allow_other,category.create=ff\ /mnt/drive1:/mnt/drive2\ /mnt/pool\f[]
+\f[C]mergerfs \-o minfreespace=4G,allow_other,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool\f[R]
 .SS Why use mergerfs over aufs?
 .PP
 aufs is mostly abandoned and no longer available in many distros.
@@ -2572,14 +2533,13 @@ features which aufs and overlayfs have.
 .PP
 UnionFS is more like aufs than mergerfs in that it offers overlay / CoW
 features.
-If you\[aq]re just looking to create a union of drives and want
+If you\[cq]re just looking to create a union of drives and want
 flexibility in file/directory placement then mergerfs offers that
 whereas unionfs is more for overlaying RW filesystems over RO ones.
 .SS Why use mergerfs over overlayfs?
 .PP
 Same reasons as with unionfs.
-.SS Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation /
-striping?
+.SS Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation / striping?
 .PP
 With simple JBOD / drive concatenation / stripping / RAID0 a single
 drive failure will result in full pool failure.
@@ -2595,8 +2555,7 @@ without the single point of failure.
 MergerFS is not intended to be a replacement for ZFS.
 MergerFS is intended to provide flexible pooling of arbitrary drives
 (local or remote), of arbitrary sizes, and arbitrary filesystems.
-For \f[C]write\ once,\ read\ many\f[] usecases such as bulk media
-storage.
+For \f[C]write once, read many\f[R] usecases such as bulk media storage.
 Where data integrity and backup is managed in other ways.
 In that situation ZFS can introduce a number of costs and limitations as
 described
@@ -2612,14 +2571,14 @@ flexibility offered by mergerfs and for some the fact it is free and
 open source is important.
 .PP
 There are a number of UnRAID users who use mergerfs as well though
-I\[aq]m not entirely familiar with the use case.
+I\[cq]m not entirely familiar with the use case.
 .SS What should mergerfs NOT be used for?
 .IP \[bu] 2
 databases: Even if the database stored data in separate files (mergerfs
-wouldn\[aq]t offer much otherwise) the higher latency of the indirection
+wouldn\[cq]t offer much otherwise) the higher latency of the indirection
 will kill performance.
 If it is a lightly used SQLITE database then it may be fine but
-you\[aq]ll need to test.
+you\[cq]ll need to test.
 .IP \[bu] 2
 VM images: For the same reasons as databases.
 VM images are accessed very aggressively and mergerfs will introduce too
@@ -2630,61 +2589,60 @@ If you need that kind of device performance aggregation or high
 availability you should stick with RAID.
 .SS Can drives be written to directly? Outside of mergerfs while pooled?
 .PP
-Yes, however it\[aq]s not recommended to use the same file from within
+Yes, however it\[cq]s not recommended to use the same file from within
 the pool and from without at the same time (particularly writing).
 Especially if using caching of any kind (cache.files, cache.entry,
 cache.attr, cache.negative_entry, cache.symlinks, cache.readdir, etc.)
 as there could be a conflict between cached data and not.
-.SS Why do I get an "out of space" / "no space left on device" / ENOSPC
-error even though there appears to be lots of space available?
+.SS Why do I get an \[lq]out of space\[rq] / \[lq]no space left on device\[rq] / ENOSPC error even though there appears to be lots of space available?
 .PP
-First make sure you\[aq]ve read the sections above about policies, path
-preservation, branch filtering, and the options \f[B]minfreespace\f[],
-\f[B]moveonenospc\f[], \f[B]statfs\f[], and \f[B]statfs_ignore\f[].
+First make sure you\[cq]ve read the sections above about policies, path
+preservation, branch filtering, and the options \f[B]minfreespace\f[R],
+\f[B]moveonenospc\f[R], \f[B]statfs\f[R], and \f[B]statfs_ignore\f[R].
 .PP
 mergerfs is simply presenting a union of the content within multiple
 branches.
 The reported free space is an aggregate of space available within the
-pool (behavior modified by \f[B]statfs\f[] and \f[B]statfs_ignore\f[]).
+pool (behavior modified by \f[B]statfs\f[R] and
+\f[B]statfs_ignore\f[R]).
 It does not represent a contiguous space.
 In the same way that read\-only filesystems, those with quotas, or
 reserved space report the full theoretical space available.
 .PP
 Due to path preservation, branch tagging, read\-only status, and
-\f[B]minfreespace\f[] settings it is perfectly valid that
-\f[C]ENOSPC\f[] / "out of space" / "no space left on device" be
-returned.
+\f[B]minfreespace\f[R] settings it is perfectly valid that
+\f[C]ENOSPC\f[R] / \[lq]out of space\[rq] / \[lq]no space left on
+device\[rq] be returned.
 It is doing what was asked of it: filtering possible branches due to
 those settings.
 Only one error can be returned and if one of the reasons for filtering a
-branch was \f[B]minfreespace\f[] then it will be returned as such.
-\f[B]moveonenospc\f[] is only relevant to writing a file which is too
+branch was \f[B]minfreespace\f[R] then it will be returned as such.
+\f[B]moveonenospc\f[R] is only relevant to writing a file which is too
 large for the drive its currently on.
 .PP
 It is also possible that the filesystem selected has run out of inodes.
-Use \f[C]df\ \-i\f[] to list the total and available inodes per
+Use \f[C]df \-i\f[R] to list the total and available inodes per
 filesystem.
 .PP
-If you don\[aq]t care about path preservation then simply change the
-\f[C]create\f[] policy to one which isn\[aq]t.
-\f[C]mfs\f[] is probably what most are looking for.
-The reason it\[aq]s not default is because it was originally set to
-\f[C]epmfs\f[] and changing it now would change people\[aq]s setup.
+If you don\[cq]t care about path preservation then simply change the
+\f[C]create\f[R] policy to one which isn\[cq]t.
+\f[C]mfs\f[R] is probably what most are looking for.
+The reason it\[cq]s not default is because it was originally set to
+\f[C]epmfs\f[R] and changing it now would change people\[cq]s setup.
 Such a setting change will likely occur in mergerfs 3.
 .SS Why does the total available space in mergerfs not equal outside?
 .PP
 Are you using ext2/3/4?
 With reserve for root?
 mergerfs uses available space for statfs calculations.
-If you\[aq]ve reserved space for root then it won\[aq]t show up.
+If you\[cq]ve reserved space for root then it won\[cq]t show up.
 .PP
-You can remove the reserve by running:
-\f[C]tune2fs\ \-m\ 0\ <device>\f[]
+You can remove the reserve by running: \f[C]tune2fs \-m 0 <device>\f[R]
 .SS Can mergerfs mounts be exported over NFS?
 .PP
 Yes, however if you do anything which may changes files out of band
-(including for example using the \f[C]newest\f[] policy) it will result
-in "stale file handle" errors unless properly setup.
+(including for example using the \f[C]newest\f[R] policy) it will result
+in \[lq]stale file handle\[rq] errors unless properly setup.
 .PP
 Be sure to use the following options:
 .IP \[bu] 2
@@ -2703,52 +2661,51 @@ to how Samba is setup in relation to permissions.
 Yes.
 .SS I notice massive slowdowns of writes when enabling cache.files.
 .PP
-When file caching is enabled in any form (\f[C]cache.files!=off\f[] or
-\f[C]direct_io=false\f[]) it will issue \f[C]getxattr\f[] requests for
-\f[C]security.capability\f[] prior to \f[I]every single write\f[].
+When file caching is enabled in any form (\f[C]cache.files!=off\f[R] or
+\f[C]direct_io=false\f[R]) it will issue \f[C]getxattr\f[R] requests for
+\f[C]security.capability\f[R] prior to \f[I]every single write\f[R].
 This will usually result in a performance degradation, especially when
 using a network filesystem (such as NFS or CIFS/SMB/Samba.)
 Unfortunately at this moment the kernel is not caching the response.
 .PP
 To work around this situation mergerfs offers a few solutions.
 .IP "1." 3
-Set \f[C]security_capability=false\f[].
-It will short circuit any call and return \f[C]ENOATTR\f[].
+Set \f[C]security_capability=false\f[R].
+It will short circuit any call and return \f[C]ENOATTR\f[R].
 This still means though that mergerfs will receive the request before
-every write but at least it doesn\[aq]t get passed through to the
+every write but at least it doesn\[cq]t get passed through to the
 underlying filesystem.
 .IP "2." 3
-Set \f[C]xattr=noattr\f[].
-Same as above but applies to \f[I]all\f[] calls to getxattr.
-Not just \f[C]security.capability\f[].
-This will not be cached by the kernel either but mergerfs\[aq] runtime
+Set \f[C]xattr=noattr\f[R].
+Same as above but applies to \f[I]all\f[R] calls to getxattr.
+Not just \f[C]security.capability\f[R].
+This will not be cached by the kernel either but mergerfs\[cq] runtime
 config system will still function.
 .IP "3." 3
-Set \f[C]xattr=nosys\f[].
-Results in mergerfs returning \f[C]ENOSYS\f[] which \f[I]will\f[] be
+Set \f[C]xattr=nosys\f[R].
+Results in mergerfs returning \f[C]ENOSYS\f[R] which \f[I]will\f[R] be
 cached by the kernel.
 No future xattr calls will be forwarded to mergerfs.
 The downside is that also means the xattr based config and query
-functionality won\[aq]t work either.
+functionality won\[cq]t work either.
 .IP "4." 3
 Disable file caching.
-If you aren\[aq]t using applications which use \f[C]mmap\f[] it\[aq]s
+If you aren\[cq]t using applications which use \f[C]mmap\f[R] it\[cq]s
 probably simpler to just disable it all together.
-The kernel won\[aq]t send the requests when caching is disabled.
+The kernel won\[cq]t send the requests when caching is disabled.
 .SS What are these .fuse_hidden files?
 .PP
 Please upgrade.
 mergerfs >= 2.26.0 will not have these temporary files.
-See the notes on \f[C]unlink\f[].
-.SS It\[aq]s mentioned that there are some security issues with mhddfs.
-What are they? How does mergerfs address them?
+See the notes on \f[C]unlink\f[R].
+.SS It\[cq]s mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
 .PP
 mhddfs (https://github.com/trapexit/mhddfs) manages running as
-\f[B]root\f[] by calling
+\f[B]root\f[R] by calling
 getuid() (https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319)
-and if it returns \f[B]0\f[] then it will
+and if it returns \f[B]0\f[R] then it will
 chown (http://linux.die.net/man/1/chown) the file.
-Not only is that a race condition but it doesn\[aq]t handle other
+Not only is that a race condition but it doesn\[cq]t handle other
 situations.
 Rather than attempting to simulate POSIX ACL behavior the proper way to
 manage this is to use seteuid (http://linux.die.net/man/2/seteuid) and
@@ -2759,10 +2716,10 @@ This is what mergerfs does and why mergerfs should always run as root.
 In Linux setreuid syscalls apply only to the thread.
 GLIBC hides this away by using realtime signals to inform all threads to
 change credentials.
-Taking after \f[B]Samba\f[], mergerfs uses
-\f[B]syscall(SYS_setreuid,...)\f[] to set the callers credentials for
+Taking after \f[B]Samba\f[R], mergerfs uses
+\f[B]syscall(SYS_setreuid,\&...)\f[R] to set the callers credentials for
 that thread only.
-Jumping back to \f[B]root\f[] as necessary should escalated privileges
+Jumping back to \f[B]root\f[R] as necessary should escalated privileges
 be needed (for instance: to clone paths between drives).
 .PP
 For non\-Linux systems mergerfs uses a read\-write lock and changes
@@ -2772,10 +2729,10 @@ to change the processes credentials.
 So long as the other threads need to be user X they will take a readlock
 allowing multiple threads to share the credentials.
 Once a request comes in to run as user Y that thread will attempt a
-write lock and change to Y\[aq]s credentials when it can.
+write lock and change to Y\[cq]s credentials when it can.
 If the ability to give writers priority is supported then that flag will
-be used so threads trying to change credentials don\[aq]t starve.
-This isn\[aq]t the best solution but should work reasonably well
+be used so threads trying to change credentials don\[cq]t starve.
+This isn\[cq]t the best solution but should work reasonably well
 assuming there are few users.
 .SH SUPPORT
 .PP
@@ -2783,7 +2740,7 @@ Filesystems are complex and difficult to debug.
 mergerfs, while being just a proxy of sorts, is also very difficult to
 debug given the large number of possible settings it can have itself and
 the massive number of environments it can run in.
-When reporting on a suspected issue \f[B]please, please\f[] include as
+When reporting on a suspected issue \f[B]please, please\f[R] include as
 much of the below information as possible otherwise it will be difficult
 or impossible to diagnose.
 Also please make sure to read all of the above documentation as it
@@ -2793,38 +2750,45 @@ includes nearly every known system or user issue previously encountered.
 release (https://github.com/trapexit/mergerfs/releases) or have tried it
 in comparison. Old versions, which are often included in distros like
 Debian and Ubuntu, are not ever going to be updated and your bug may
-have been addressed already.\f[]
+have been addressed already.\f[R]
+.PP
+\f[B]For commercial support or feature requests please contact me
+directly.\f[R]
 .SS Information to include in bug reports
 .IP \[bu] 2
-Version of mergerfs: \f[C]mergerfs\ \-V\f[]
+Version of mergerfs: \f[C]mergerfs \-V\f[R]
 .IP \[bu] 2
 mergerfs settings / arguments: from fstab, systemd unit, command line,
 etc.
 .IP \[bu] 2
-Version of the OS: \f[C]uname\ \-a\f[]
+Version of the OS: \f[C]uname \-a\f[R]
 .IP \[bu] 2
 List of branches, their filesystem types, sizes (before and after
-issue): \f[C]df\ \-h\f[]
+issue): \f[C]df \-h\f[R]
 .IP \[bu] 2
-\f[B]All\f[] information about the relevant branches and paths:
+\f[B]All\f[R] information about the relevant branches and paths:
 permissions, ownership, etc.
 .IP \[bu] 2
-\f[B]All\f[] information about the client app making the requests:
+\f[B]All\f[R] information about the client app making the requests:
 version, uid/gid
 .IP \[bu] 2
 Runtime environment: mostly are things running inside containers or not
 .IP \[bu] 2
-A \f[C]strace\f[] of the app having problems:
+A \f[C]strace\f[R] of the app having problems:
+.RS 2
 .IP \[bu] 2
-\f[C]strace\ \-fvTtt\ \-s\ 256\ \-o\ /tmp/app.strace.txt\ <cmd>\f[]
+\f[C]strace \-fvTtt \-s 256 \-o /tmp/app.strace.txt <cmd>\f[R]
+.RE
 .IP \[bu] 2
-A \f[C]strace\f[] of mergerfs while the program is trying to do whatever
-it\[aq]s failing to do:
+A \f[C]strace\f[R] of mergerfs while the program is trying to do
+whatever it\[cq]s failing to do:
+.RS 2
 .IP \[bu] 2
-\f[C]strace\ \-fvTtt\ \-s\ 256\ \-p\ <mergerfsPID>\ \-o\ /tmp/mergerfs.strace.txt\f[]
+\f[C]strace \-fvTtt \-s 256 \-p <mergerfsPID> \-o /tmp/mergerfs.strace.txt\f[R]
+.RE
 .IP \[bu] 2
-\f[B]Precise\f[] directions on replicating the issue.
-Do not leave \f[B]anything\f[] out.
+\f[B]Precise\f[R] directions on replicating the issue.
+Do not leave \f[B]anything\f[R] out.
 .IP \[bu] 2
 Try to recreate the problem in the simplest way using standard programs:
 ln, mv, cp, ls, dd, etc.
@@ -2832,7 +2796,7 @@ ln, mv, cp, ls, dd, etc.
 .IP \[bu] 2
 github.com: https://github.com/trapexit/mergerfs/issues
 .IP \[bu] 2
-email: trapexit\@spawn.link
+email: trapexit\[at]spawn.link
 .IP \[bu] 2
 discord: https://discord.gg/MpAr69V
 .IP \[bu] 2
@@ -2841,72 +2805,12 @@ twitter: https://twitter.com/_trapexit
 reddit: https://www.reddit.com/user/trapexit
 .SS Support development
 .PP
-This software is free to use and released under a very liberal license
-(ISC).
-That said if you like this software and would like to support its
-development donations are welcome.
+This software is released under the very liberal ISC license and is
+therefore free to use for personal or commercial uses.
+That said if you like this software and have the means please consider
+supporting its development.
 .PP
-Crypto is fine in whatever protocol you prefer.
-My preferences for fiat would be GitHub Sponsors or PayPal though feel
-free to use any platform listed below.
-.IP \[bu] 2
-GitHub Sponsors: https://github.com/sponsors/trapexit
-.IP \[bu] 2
-PayPal: https://paypal.me/trapexit
-.IP \[bu] 2
-Patreon: https://www.patreon.com/trapexit
-.IP \[bu] 2
-BuyMeACoffee: https://buymeacoff.ee/trapexit
-.IP \[bu] 2
-Ko\-Fi: https://ko\-fi.com/trapexit
-.IP \[bu] 2
-Open Collective: https://opencollective.com/trapexit
-.IP \[bu] 2
-Bitcoin (BTC): bc1qu537hqlnmn2wawx9n7nws0dlkz55h0cd93ny28
-.IP \[bu] 2
-Bitcoin Cash (BCH):
-bitcoincash:qqp0vh9v44us74gaggwjfv9y54zfjmmd7srlqxa3xt
-.IP \[bu] 2
-Bitcoin SV (BSV): 1FkFuxRtt3f8LbkpeUKRZq7gKJFzGSGgZV
-.IP \[bu] 2
-Bitcoin Gold (BTG): AaPuJgJeohPjkB3LxJM6NKGnaHoRJ8ieT3
-.IP \[bu] 2
-Litecoin (LTC): MJQzsHBdNnkyGqCFdcAdHYKugicBmfAXfQ
-.IP \[bu] 2
-Dogecoin (DOGE): DLJNLVe28vZ4SMQSxDJLBQBv57rGtUoWFh
-.IP \[bu] 2
-Ethereum (ETH): 0xB8d6d55c0319aacC327860d13f891427caEede7a
-.IP \[bu] 2
-Any ERC20 Token: 0xB8d6d55c0319aacC327860d13f891427caEede7a
-.IP \[bu] 2
-Ethereum Classic (ETC): 0x2B6054428e69a1201B6555f7a2aEc0Fba01EAD9F
-.IP \[bu] 2
-Harmony (ONE): one1hrtd2hqrrx4vcvncvrgnlzg5yl9wahn66lq6rw
-(0xB8d6d55c0319aacC327860d13f891427caEede7a)
-.IP \[bu] 2
-Monero (XMR):
-45BBZMrJwPSaFwSoqLVNEggWR2BJJsXxz7bNz8FXnnFo3GyhVJFSCrCFSS7zYwDa9r1TmFmGMxQ2HTntuc11yZ9q1LeCE8f
-.IP \[bu] 2
-Dash (DASH): XvsFrohu8tbjA4E8p7xsc86E2ADxLHGXHL
-.IP \[bu] 2
-Chia (XCH):
-xch18l7e2q34jtuyzkq0jg8vp7yrtnfwzly30w3yyhkk96um2ur4yqcq6p2een
-.IP \[bu] 2
-LBRY Credits (LBC): bFusyoZPkSuzM2Pr8mcthgvkymaosJZt5r
-.IP \[bu] 2
-Ripple (XRP): r9f6aoxaGD8aymxqH89Ke1PCUPkNiFdZZC
-.IP \[bu] 2
-Tezos (XTZ): tz1ZxerkbbALsuU9XGV9K9fFpuLWnKAGfc1C
-.IP \[bu] 2
-Zcash (ZEC): t1Zo1GGn2T3GrhKvgdtnTsTnWu6tCPaCaHG
-.IP \[bu] 2
-DigiByte (DGB): Sb8r1qTrryY9Sp4YkTE1eeKEGVzgArnE5N
-.IP \[bu] 2
-Namecoin (NMC): NDzb9FkoptGu5QbgetCkodJqo2zE1cTwyb
-.IP \[bu] 2
-Vertcoin (VTC): 3PYdhokAGXJwWrwHRoTywxG4iUDk6EHjKe
-.IP \[bu] 2
-Other crypto currencies: contact me for address
+https://github.com/trapexit/support
 .SH LINKS
 .IP \[bu] 2
 https://spawn.link
diff --git a/src/category.cpp b/src/category.cpp
index e1fd7f2..cc3e52f 100644
--- a/src/category.cpp
+++ b/src/category.cpp
@@ -42,7 +42,7 @@ Category::Base::to_string(void) const
 {
   std::set<std::string> rv;
 
-  for(auto func : funcs)
+  for(const auto func : funcs)
     rv.insert(func->to_string());
 
   return str::join(rv,',');
diff --git a/src/config.cpp b/src/config.cpp
index fd10f71..a9e3793 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -107,7 +107,8 @@ Config::Config()
     statfs_ignore(StatFSIgnore::ENUM::NONE),
     symlinkify(false),
     symlinkify_timeout(3600),
-    threads(0),
+    fuse_read_thread_count(-1),
+    fuse_process_thread_count(-1),
     version(MERGERFS_VERSION),
     writeback_cache(false),
     xattr(XAttr::ENUM::PASSTHROUGH)
@@ -173,7 +174,9 @@ Config::Config()
   _map["statfs_ignore"]        = &statfs_ignore;
   _map["symlinkify"]           = &symlinkify;
   _map["symlinkify_timeout"]   = &symlinkify_timeout;
-  _map["threads"]              = &threads;
+  _map["threads"]              = &fuse_read_thread_count;
+  _map["read-thread-count"]    = &fuse_read_thread_count;
+  _map["process-thread-count"] = &fuse_process_thread_count;
   _map["version"]              = &version;
   _map["xattr"]                = &xattr;
 }
diff --git a/src/config.hpp b/src/config.hpp
index fe3ce3e..5e3a796 100644
--- a/src/config.hpp
+++ b/src/config.hpp
@@ -138,7 +138,8 @@ public:
   StatFSIgnore   statfs_ignore;
   ConfigBOOL     symlinkify;
   ConfigUINT64   symlinkify_timeout;
-  ConfigINT      threads;
+  ConfigINT      fuse_read_thread_count;
+  ConfigINT      fuse_process_thread_count;
   ConfigSTR      version;
   ConfigBOOL     writeback_cache;
   XAttr          xattr;
diff --git a/src/func.cpp b/src/func.cpp
index 39aa719..1c6065c 100644
--- a/src/func.cpp
+++ b/src/func.cpp
@@ -23,6 +23,8 @@ int
 Func::Base::Action::from_string(const std::string &policyname_)
 {
   policy = Policies::Action::find(policyname_);
+  if(!policy)
+    return -EINVAL;
 
   return 0;
 }
@@ -37,6 +39,8 @@ int
 Func::Base::Create::from_string(const std::string &policyname_)
 {
   policy = Policies::Create::find(policyname_);
+  if(!policy)
+    return -EINVAL;
 
   return 0;
 }
@@ -51,6 +55,8 @@ int
 Func::Base::Search::from_string(const std::string &policyname_)
 {
   policy = Policies::Search::find(policyname_);
+  if(!policy)
+    return -EINVAL;
 
   return 0;
 }
diff --git a/src/fuse_getattr.cpp b/src/fuse_getattr.cpp
index e7431be..13b5ccb 100644
--- a/src/fuse_getattr.cpp
+++ b/src/fuse_getattr.cpp
@@ -139,7 +139,7 @@ namespace l
       return -errno;
 
     if(symlinkify_ && symlinkify::can_be_symlink(*st_,symlinkify_timeout_))
-      st_->st_mode = symlinkify::convert(st_->st_mode);
+      symlinkify::convert(fullpath,st_);
 
     fs::inode::calc(fusepath_,st_);
 
diff --git a/src/ghc/filesystem.hpp b/src/ghc/filesystem.hpp
index 4924c73..d178dea 100644
--- a/src/ghc/filesystem.hpp
+++ b/src/ghc/filesystem.hpp
@@ -214,7 +214,6 @@
 #include <compare>
 #endif
 #endif
-#include <chrono>
 #include <fstream>
 #include <memory>
 #include <stack>
@@ -265,7 +264,7 @@
 // configure LWG conformance ()
 #define LWG_2682_BEHAVIOUR
 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular
+// LWG #2395 makes create_directory/create_directories not emit an error if there is a regular
 // file with that name, it is superseded by P1164R1, so only activate if really needed
 // #define LWG_2935_BEHAVIOUR
 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -3233,7 +3232,7 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons
             // we can only sit on a slash if it is a network name or a root
             if (i != _last && *i == preferred_separator) {
                 if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) {
-                    // leadind double slashes detected, treat this and the
+                    // leading double slashes detected, treat this and the
                     // following until a slash as one unit
                     i = std::find(++i, _last, preferred_separator);
                 }
diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp
index 56a52e8..f947c25 100644
--- a/src/mergerfs.cpp
+++ b/src/mergerfs.cpp
@@ -66,7 +66,7 @@
 #include "fuse_truncate.hpp"
 #include "fuse_unlink.hpp"
 #include "fuse_utimens.hpp"
-#include "fuse_write_buf.hpp"
+#include "fuse_write.hpp"
 
 #include "fuse.h"
 
@@ -129,7 +129,7 @@ namespace l
     ops_.truncate        = FUSE::truncate;
     ops_.unlink          = FUSE::unlink;
     ops_.utimens         = FUSE::utimens;
-    ops_.write_buf       = (nullrw_ ? FUSE::write_buf_null : FUSE::write_buf);
+    ops_.write           = (nullrw_ ? FUSE::write_null : FUSE::write);
 
     return;
   }
@@ -164,7 +164,10 @@ namespace l
 
     options::parse(&args,&errs);
     if(errs.size())
-      std::cerr << errs << std::endl;
+      {
+        std::cerr << errs << std::endl;
+        return 1;
+      }
 
     l::setup_resources();
     l::get_fuse_operations(ops,cfg->nullrw);
diff --git a/src/num.cpp b/src/num.cpp
index 667be09..7bfaf89 100644
--- a/src/num.cpp
+++ b/src/num.cpp
@@ -16,17 +16,18 @@
 
 #include "ef.hpp"
 
-#include <cstdint>
-#include <string>
-
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
 
-#define KB (1024UL)
-#define MB (KB * 1024UL)
-#define GB (MB * 1024UL)
-#define TB (GB * 1024UL)
+#include <cstdint>
+#include <string>
+
+#define KB (1024ULL)
+#define MB (KB * 1024ULL)
+#define GB (MB * 1024ULL)
+#define TB (GB * 1024ULL)
 
 
 namespace num
@@ -116,17 +117,17 @@ namespace num
     char buf[64];
 
     if(bytes_ < KB)
-      sprintf(buf,"%lu",bytes_);
+      sprintf(buf,"%" PRIu64 "",bytes_);
     ef(((bytes_ / TB) * TB) == bytes_)
-      sprintf(buf,"%luT",bytes_ / TB);
+      sprintf(buf,"%" PRIu64 "T",bytes_ / TB);
     ef(((bytes_ / GB) * GB) == bytes_)
-      sprintf(buf,"%luG",bytes_ / GB);
+      sprintf(buf,"%" PRIu64 "G",bytes_ / GB);
     ef(((bytes_ / MB) * MB) == bytes_)
-      sprintf(buf,"%luM",bytes_ / MB);
+      sprintf(buf,"%" PRIu64 "M",bytes_ / MB);
     ef(((bytes_ / KB) * KB) == bytes_)
-      sprintf(buf,"%luK",bytes_ / KB);
+      sprintf(buf,"%" PRIu64 "K",bytes_ / KB);
     else
-      sprintf(buf,"%lu",bytes_);
+      sprintf(buf,"%" PRIu64 "",bytes_);
 
     return std::string(buf);
   }
diff --git a/src/option_parser.cpp b/src/option_parser.cpp
index 519b52c..df719f7 100644
--- a/src/option_parser.cpp
+++ b/src/option_parser.cpp
@@ -49,27 +49,6 @@ enum
   };
 
 
-namespace l
-{
-  static
-  int
-  calculate_thread_count(int threads_)
-  {
-    int tc;
-
-    if(threads_ > 0)
-      return threads_;
-
-    tc = hw::cpu::logical_core_count();
-    if(threads_ < 0)
-      tc /= -threads_;
-    if(tc == 0)
-      tc = 1;
-
-    return tc;
-  }
-}
-
 static
 void
 set_option(const std::string &option_,
@@ -95,16 +74,11 @@ set_kv_option(const std::string &key_,
 
 static
 void
-set_threads(Config::Write &cfg_,
-            fuse_args     *args_)
+set_fuse_threads(Config::Write &cfg_,
+                      fuse_args     *args_)
 {
-  int threads;
-
-  threads = l::calculate_thread_count(cfg_->threads);
-
-  cfg_->threads = threads;
-
-  set_kv_option("threads",cfg_->threads.to_string(),args_);
+  set_kv_option("read-thread-count",cfg_->fuse_read_thread_count.to_string(),args_);
+  set_kv_option("process-thread-count",cfg_->fuse_process_thread_count.to_string(),args_);
 }
 
 static
@@ -139,6 +113,24 @@ set_default_options(fuse_args *args_)
   set_option("default_permissions",args_);
 }
 
+static
+bool
+should_ignore(const std::string &key_)
+{
+  static const std::set<std::string> IGNORED_KEYS =
+    {
+      "atomic_o_trunc",
+      "big_writes",
+      "cache.open",
+      "defaults",
+      "hard_remove",
+      "nonempty",
+      "use_ino"
+    };
+
+  return (IGNORED_KEYS.find(key_) != IGNORED_KEYS.end());
+}
+
 static
 int
 parse_and_process_kv_arg(Config::Write     &cfg_,
@@ -169,15 +161,7 @@ parse_and_process_kv_arg(Config::Write     &cfg_,
     val = "true";
   ef(key == "sync_read" && val.empty())
     {key = "async_read", val = "false";}
-  ef(key == "defaults")
-    return 0;
-  ef(key == "hard_remove")
-    return 0;
-  ef(key == "atomic_o_trunc")
-    return 0;
-  ef(key == "big_writes")
-    return 0;
-  ef(key == "cache.open")
+  ef(::should_ignore(key_))
     return 0;
 
   if(cfg_->has_key(key) == false)
@@ -287,8 +271,6 @@ usage(void)
     "    -o cache.negative_entry=INT\n"
     "                           Negative file name lookup cache timeout in\n"
     "                           seconds. default = 0\n"
-    "    -o use_ino             Have mergerfs generate inode values rather than\n"
-    "                           autogenerated by libfuse. Suggested.\n"
     "    -o inodecalc=passthrough|path-hash|devino-hash|hybrid-hash\n"
     "                           Selects the inode calculation algorithm.\n"
     "                           default = hybrid-hash\n"
@@ -413,6 +395,6 @@ namespace options
     set_default_options(args_);
     set_fsname(cfg,args_);
     set_subtype(args_);
-    set_threads(cfg,args_);
+    set_fuse_threads(cfg,args_);
   }
 }
diff --git a/src/policy.hpp b/src/policy.hpp
index e527b94..de7a4bf 100644
--- a/src/policy.hpp
+++ b/src/policy.hpp
@@ -73,6 +73,11 @@ namespace Policy
       return (*impl)(branches_,fusepath_.c_str(),paths_);
     }
 
+    operator bool() const
+    {
+      return (bool)impl;
+    }
+
   private:
     ActionImpl *impl;
   };
@@ -134,6 +139,11 @@ namespace Policy
       return (*impl)(branches_,fusepath_.c_str(),paths_);
     }
 
+    operator bool() const
+    {
+      return (bool)impl;
+    }
+
   private:
     CreateImpl *impl;
   };
@@ -188,6 +198,11 @@ namespace Policy
       return (*impl)(branches_,fusepath_.c_str(),paths_);
     }
 
+    operator bool() const
+    {
+      return (bool)impl;
+    }
+
   private:
     SearchImpl *impl;
   };
diff --git a/src/policy_lfs.cpp b/src/policy_lfs.cpp
index ace2a1b..9a93100 100644
--- a/src/policy_lfs.cpp
+++ b/src/policy_lfs.cpp
@@ -40,7 +40,6 @@ namespace lfs
     int error;
     uint64_t lfs;
     fs::info_t info;
-    const Branch *branch;
     const string *basepath;
 
     error = ENOENT;
diff --git a/src/rnd.cpp b/src/rnd.cpp
index c30f3a7..9a432d5 100644
--- a/src/rnd.cpp
+++ b/src/rnd.cpp
@@ -34,7 +34,9 @@ RND::RND()
 
   gettimeofday(&tv,NULL);
 
-  G_SEED = ((tv.tv_sec << 32) | (tv.tv_usec));
+  G_SEED   = tv.tv_sec;
+  G_SEED <<= 32;
+  G_SEED  |= tv.tv_usec;
 }
 
 uint64_t
diff --git a/src/symlinkify.hpp b/src/symlinkify.hpp
index 628e172..5f81eff 100644
--- a/src/symlinkify.hpp
+++ b/src/symlinkify.hpp
@@ -21,6 +21,9 @@
 #include <sys/stat.h>
 #include <time.h>
 
+#include <string>
+
+
 namespace symlinkify
 {
   static
@@ -41,9 +44,12 @@ namespace symlinkify
 
   static
   inline
-  mode_t
-  convert(const mode_t mode_)
+  void
+  convert(const std::string &target_,
+          struct stat       *st_)
   {
-    return ((mode_ & ~S_IFMT) | S_IFLNK);
+    st_->st_mode = (((st_->st_mode & ~S_IFMT) | S_IFLNK) | 0777);
+    st_->st_size = target_.size();
+    st_->st_blocks = 0;
   }
 }
diff --git a/src/version.hpp b/src/version.hpp
deleted file mode 100644
index 19113e9..0000000
--- a/src/version.hpp
+++ /dev/null
@@ -1,2 +0,0 @@
-#pragma once
-static const char MERGERFS_VERSION[] = "2.33.5";
diff --git a/src/wyhash.h b/src/wyhash.h
index b5b85f4..daae691 100644
--- a/src/wyhash.h
+++ b/src/wyhash.h
@@ -3,7 +3,7 @@
 #define wyhash_final_version
 //defines that change behavior
 #ifndef WYHASH_CONDOM
-#define WYHASH_CONDOM 1 //0: read 8 bytes before and after boudaries, dangerous but fastest. 1: normal valid behavior 2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication"
+#define WYHASH_CONDOM 1 //0: read 8 bytes before and after boundaries, dangerous but faster. 1: normal valid behavior 2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication"
 #endif
 #define WYHASH_32BIT_MUM 0	//faster on 32 bit system
 //includes
diff --git a/tests/acutest.h b/tests/acutest.h
index 8b49afc..aec9f7f 100644
--- a/tests/acutest.h
+++ b/tests/acutest.h
@@ -62,7 +62,7 @@
 #define TEST_LIST               const struct test_ test_list_[]
 
 
-/* Macros for testing whether an unit test succeeds or fails. These macros
+/* Macros for testing whether a unit test succeeds or fails. These macros
  * can be used arbitrarily in functions implementing the unit tests.
  *
  * If any condition fails throughout execution of a test, the test fails.
@@ -245,7 +245,7 @@
 #endif
 
 
-/* Common test initialiation/clean-up
+/* Common test initialization/clean-up
  *
  * In some test suites, it may be needed to perform some sort of the same
  * initialization and/or clean-up in all the tests.
diff --git a/tools/create-branches b/tools/create-branches
new file mode 100755
index 0000000..4a893e4
--- /dev/null
+++ b/tools/create-branches
@@ -0,0 +1,14 @@
+#!/bin/sh
+set -x
+
+BASEPATH="/tmp"
+
+for x in $(seq -w 2)
+do
+    FILEPATH="${BASEPATH}/mergerfs-${x}.img"
+    MOUNTPOINT="${BASEPATH}/mergerfs-${x}"
+    truncate -s 1G "${FILEPATH}"
+    mkdir -p "${MOUNTPOINT}"
+    mkfs.ext4 -m0 -L "mergerfs${x}" "${FILEPATH}"
+    sudo mount "${FILEPATH}" "${MOUNTPOINT}"
+done    

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/debug/.build-id/6c/c0a4c4f684db85fbe2e7e3f71fa027a78d0b3a.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/be/e6bdec8768ce965307b07197c2480e8cd7e140.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/ee/b86566717deaf0375433a3c38f6ab2f48b943d.debug

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/debug/.build-id/46/7e0a5127263c6dca101401119924ef4ea7a765.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/6c/77785b103b048de7c1f92cfef2228b877cd052.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/7c/6191a567a16da6ecfd92ff818176353aafb38f.debug
-rw-r--r--  root/root   /usr/share/doc/mergerfs/changelog.gz

Control files of package mergerfs: lines which differ (wdiff format)

  • Depends: libc6 (>= 2.34), libgcc-s1 (>= 3.0), libstdc++6 (>= 11), 12), libfuse2, fuse

Control files of package mergerfs-dbgsym: lines which differ (wdiff format)

  • Build-Ids: 467e0a5127263c6dca101401119924ef4ea7a765 6c77785b103b048de7c1f92cfef2228b877cd052 7c6191a567a16da6ecfd92ff818176353aafb38f 6cc0a4c4f684db85fbe2e7e3f71fa027a78d0b3a bee6bdec8768ce965307b07197c2480e8cd7e140 eeb86566717deaf0375433a3c38f6ab2f48b943d

More details

Full run details