Compare commits

...
200 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 836ed92a6f libfuse: rewrite remembered node retention 2026-05-04 07:33:20 -05:00
Antonio SJ Musumeci 230b194291 config: make fuse runtime flags atomic 2026-05-03 23:40:52 -05:00
Antonio SJ Musumeci fae31d68c2 libfuse: tighten scoped mutex locking 2026-05-03 23:40:39 -05:00
Antonio SJ Musumeci 3ff9794fff libfuse: fix use-after-free in fuse_lib_ioctl 2026-04-30 12:51:12 -05:00
Antonio SJ Musumeci f71e27644e vendor moodycamel queue and upgrade to 1.0.5 2026-04-30 08:43:42 -04:00
Antonio SJ Musumeci 943f91ff98 Strengthen mergerfs integration tests
- Make the test runner fail on failed tests and report explicit skips
- Strengthen assertions for O_DIRECT, LUP policy, EXDEV fallback, and xattrs
- Fix cleanup and file descriptor handling across test scripts
2026-04-29 20:25:20 -04:00
Antonio SJ Musumeci ef1e01b5a5 Add unit and integration tests for recently changed code
Unit tests (29 new in tests/tests.cpp):
- str::eq() and str::startswith() char* nullptr safety
- str::from() u64/s64/int suffix parsing (K/M/G/T, overflow, invalid)
- num::humanize() byte formatting (0, 512, 1K, 1M, 1G, 1T, non-exact)
- RmdirErr priority error accumulation (ENOTEMPTY/EEXIST override rules)
- HashSet inline-to-heap growth, empty string, and large insertion
- fs::inode algorithm switching (valid/invalid names, passthrough raw ino,
  distinct inodes across algorithms)
- RND::rand64() bounds (range, max, shrink_to_rand_elem)
- Branches::Impl copy and move assignment re-links default_minfreespace

Integration tests (5 new in tests/):
- TEST_policy_lup: LUP policy create and search
- TEST_rmdir_enotempty_priority: rmdir returns ENOTEMPTY for non-empty dirs
- TEST_open_after_unlink: read/write via fd after unlink
- TEST_api_xattr: mergerfs xattr API (fullpath, relpath, basepath, allpaths)
- TEST_movefile_enospc: moveonenospc configuration round-trip

Also: remove xattr nosys checks from TEST_cfg_xattr_modes, reformat
integration test scripts to use temp_dir/cleanup_dir helpers, and
minor parameter alignment changes in fuse_getxattr, fuse_init,
fuse_rename.
2026-04-29 17:41:00 -04:00
Antonio SJ Musumeci 50c0627b2b Better handle make sanitize option 2026-04-29 12:24:10 -04:00
Antonio SJ Musumeci 660f556ac6 Don't return success on ENOTEMPTY rmdir errors 2026-04-29 12:23:49 -04:00
Antonio SJ Musumeci 997b036064 Reject invalid policies 2026-04-29 00:34:00 -04:00
Antonio SJ Musumeci 9308a3729c When file unlinked check open_files map
Inadvertantly removed. Didn't realize that the kernel would not always
send file handle when unlinked.
2026-04-28 16:04:26 -04:00
Antonio SJ Musumeci 7616d8b1ac Rework readlink to not depend on cstr nul termination 2026-04-26 01:37:08 -04:00
Antonio SJ Musumeci a86092b3c2 Don't push docs for RC tags 2026-04-23 20:46:55 -05:00
Antonio SJ Musumeci e613d9415f Better same file check for fsck 2026-04-22 22:28:56 -05:00
Antonio SJ Musumeci 1a8eb326af Tweak docs on NFS and identity management 2026-04-21 13:20:07 -05:00
Antonio SJ Musumeci 9fb1642e93 Fix docs regarding "noforget" 2026-04-20 23:25:47 -05:00
Antonio SJ Musumeci 8be86ee611 Remove mkdocs build github workflow 2026-04-19 23:48:13 -05:00
Antonio S. Musumeci da68cf3a85 Formatting: normalize brace style and parameter alignment
No functional change: libfuse/helper.cpp (fuse_daemonize,
fuse_helper_opt_proc, fuse_parse_cmdline) and mergerfs
fuse_init.cpp (_want helpers) reformatted to match the project's
GNU-style brace placement and aligned parameter lists.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci ec40866091 libfuse: small safety and portability fixes across opt/cpu/fusermount/maintenance_thread
- fuse_opt: reformat process_opt_param to project style; return the
  rv from sscanf through a named local so the error branch prints
  the failing option cleanly.

- cpu.cpp: when parsing /sys/devices/system/cpu/*/topology/core_id,
  bail out cleanly on stream read failure rather than recording an
  uninitialized core_id.

- fusermount: free(copy) on the success path of parsing the mount
  option (previously leaked on every mount), and replace the fixed-
  buffer sprintf into procfile with snprintf bounded by the buffer
  size.

- maintenance_thread: widen the iteration count from int to u64
  so a long-lived daemon doesn't wrap after ~68 years of ticks
  (real effect: silence signed-overflow warnings under UBSan).
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci bd0714272e libfuse: thread_pool/objpool/fuse_loop - safer memory and iteration
- ThreadPool::destroy used to iterate _threads directly while another
  thread could be adjusting the same vector. Snapshot the vector
  under the mutex and iterate the local copy for pthread_cancel /
  pthread_join.

- ObjPool::construct now returns NULL when the pooled allocator
  returns NULL instead of invoking placement-new on a null
  pointer. In destroy, compute to_node(obj) before running the
  destructor - the destructor may scrub bytes the to_node helper
  reads.

- In the async/sync workers in fuse_loop, msgbuf_alloc can return
  NULL under memory pressure. Fail the worker cleanly with a
  diagnostic rather than dereferencing. Also introduce a small
  _nproc() helper that treats hardware_concurrency()==0 as 1, so
  the downstream min/max arithmetic doesn't produce a zero thread
  count.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 27acd9fcc7 libfuse: use atomic fuse_session pointer in signal handler
The signal handler reads the global session pointer from an async-
signal context while another thread installs/removes the session
via set/remove_signal_handlers. Use std::atomic<fuse_session*> with
acquire/release ordering so the handler either sees NULL or a fully
published session, never a torn pointer.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 4516685ca7 libfuse: harden fuse.cpp against NULL nodes; use lock guards; check allocations
Collapse get_node/get_node_nocheck into a single function that
returns NULL when the node is absent rather than abort()ing.
Callers that previously assumed non-NULL now check the return value
and either reply ESTALE/ENOMEM or fall through gracefully; a few
places where the node is load-bearing (e.g. unhash during remove)
use fatal::abort with a descriptive message.

Switch mutex_lock / mutex_unlock pairs to mutex_lockguard so early
returns on error paths no longer leak the lock, and add NULL-safety
around fuse_dh in readdir/readdir_plus, fuse_dirents_init error
returns, the getattr path when both name and fh are empty, and the
msgbuf allocation in fuse_lib_read.

Pull the new xstrdup/xmalloc wrappers from xalloc.hpp for the root
node name so the common-case OOM path terminates with a diagnostic
instead of a silent corrupted node.

Also guard fuse_session_destroy against a NULL session - called
from error-recovery paths.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 0bdbaf1d6c libfuse: remove userdata from init/destroy APIs; drop unused fuse_reply_ioctl_iov
The low-level init/destroy callbacks were passed a userdata pointer
that mergerfs never consumed - the caller threaded it through
fuse_lowlevel_new only to hand it back unchanged. Remove the
parameter from the ops struct, from fuse_lowlevel_new(_common),
and from the internal fuse_ll struct; update fuse_lib_init and
fuse_lib_destroy accordingly.

Also drop fuse_reply_ioctl_iov: no caller in-tree invokes it and
the public API is defined by this vendored libfuse fork.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci e67b7d0825 log: default debug log path to empty; skip set when empty
libfuse previously initialized its debug log filepath to
"/dev/stderr", forcing the config layer to reopen stderr on every
load whether or not the user asked for it. Default the libfuse log
path to "" and have the mergerfs config layer only forward the
request to libfuse when the user supplied an explicit path.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 981d5473ee str: null-safe same() and startswith()
Both helpers previously called strcmp/pointer-dereference directly
on their arguments, so a stray NULL (e.g. an unset env string or an
absent xattr) crashed the process. Guard both helpers: same()
returns true only if both pointers are equal, startswith() returns
false if either side is NULL.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 364f8c4407 fs_movefile_and_open: -ENOSPC when no destination branch available
The create policy can legitimately return success with an empty
destination list (e.g. every candidate rejected). Previously we
then dereferenced dst_branch[0] and faulted. Return -ENOSPC so the
original write that triggered the move propagates a sensible errno
to userspace.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci ef9c7ad5df fuse_statfs: scale with long double to avoid integer overflow
The branch-aggregation code computed
  blocks * f_frsize / min_frsize
as u64 arithmetic. With large filesystems the intermediate product
blocks * f_frsize overflows 64 bits long before the division can
bring the result back into range, producing garbage bavail/bfree
values reported to userspace.

Compute the scale factor once in long double and multiply each
count by it, so the only narrowing happens in the final cast back
to fsblkcnt_t.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 4288a60c5d fuse_readlink: always null-terminate buffer
Old code only null-terminated when the returned/copied length was
strictly less than the caller-supplied bufsize_, leaving the buffer
unterminated on an exact-fill. Since buf_ is consumed as a C string,
reserve one byte for the terminator up front and unconditionally
terminate.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci cf38a37176 fuse_readdir: atomic initialized flag; bound error string; check msgbuf allocation
- ReadDir::_initialized is set by the first thread through the
  lazy-init path and read by every subsequent caller; make it
  std::atomic<bool> so the read/write pair has a happens-before
  relationship.

- The 'no valid mergerfs branch found' error directory entry used
  strcpy into a fixed-size dirent buffer. The message happens to
  fit today but the buffer is only NAME_MAX wide; switch to a
  length-bounded strncpy driven by the constexpr string size.

- The getdents implementations for seq/cor/cosr allocated a msgbuf
  without checking for allocation failure and then dereferenced it.
  Return -ENOMEM when the allocation fails.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci f5d9a60b9d fuse_getattr/statx: populate timeouts in fake_root and controlfile replies
The fake-root and control-file fast paths returned a synthetic stat
without touching the timeout struct. Callers that took those paths
ended up sending whatever the timeout struct happened to hold,
which is uninitialized memory for the control-file path and
whatever a prior call left there for the fake-root path. Kernel
behavior is undefined when the timeout is garbage.

Pass timeout_ into both helpers and set entry/attr from the
configured cache values.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 0c17492c78 FUSE: pass Branches::Ptr by value with empty-branches guards
Previously most FUSE ops took a reference to the live cfg.branches
object. With atomic_load now producing a shared_ptr snapshot, pass
it by value: the op works against a single stable view of the
configuration for its whole lifetime, immune to concurrent
reconfiguration.

Also bail out with -ENOENT when the policy returns an empty branch
set rather than dereferencing branches[0]. This was reachable if a
policy rejected every branch (e.g. everything readonly while
looking for a writable target).

Covers access, chmod, chown, create, getattr, getxattr, ioctl,
link, listxattr, mkdir, mknod, open, readdir (cor/cosr),
readlink, removexattr, rename, rmdir, setxattr, statfs, statx,
symlink, truncate, unlink, utimens.

While here, in _getattr only dereference st_->st_mode when lstat
succeeded (it is otherwise uninitialized).
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 98648ae44b FUSE: require valid file/dir handle; drop open_files fallback
Several FUSE ops fell back to looking up a FileInfo via
state.open_files when the kernel-provided fh was zero, returning
-ENOENT if the lookup failed. This path papered over handle-tracking
bugs and added cost on every call. Use the handle directly and
return -EBADF when it is invalid - the kernel should always hand us
a valid fh for an open resource.

Also simplify FileInfo::from_fh / DirInfo::from_fh and their to_fh
helpers: callers now handle the null-pointer case explicitly so the
ASSERT_NOT_NULL checks are redundant.

Covers fallocate, fchmod, fchown, fgetattr, flock, ftruncate,
futimens, read, release, releasedir, write, statx, ioctl (file +
dir), readdir (sequential, concurrent-on-read,
concurrent-on-search), and the passthrough fast paths in
create/open.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 0dcca0f1a3 branches: re-link default_minfreespace pointer on copy/move; atomically publish branch mode changes
Branches using the default minfreespace held a reference to their
owning Impl's _default_minfreespace. After a copy or move the
pointer still referenced the source Impl, which could become
invalid or diverge after further configuration changes. Switch to
a pointer and re-link it to the destination Impl on copy/move.

While here, convert set_branch_mode to do an atomic_store of a
newly constructed Impl rather than mutating the shared pointer
target in place. Readers holding an atomic_load snapshot are no
longer affected by a concurrent mode change.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci 103cd358f2 state: atomic ref_count in OpenFile
ref_count is read and written from multiple FUSE worker threads
during open/release. Make it std::atomic<int> and provide a move
constructor so OpenFile remains movable when the underlying atomic
is non-movable.
2026-04-18 13:47:23 -05:00
Antonio S. Musumeci aa46001923 Remove unused rwlock.hpp header and includes
rwlock.hpp was left over after migrating to atomic shared pointers
for branches configuration. No code references ReadGuard/WriteGuard
any longer, so drop the header and its stale includes.
2026-04-18 13:47:23 -05:00
Antonio SJ Musumeci ff48ff6047 Handle/ignore SIGHUP 2026-04-17 11:14:05 -05:00
Antonio SJ Musumeci d5a2187713 Fix make race condition 2026-04-16 18:20:00 -05:00
Antonio S. Musumeci 351adc2b20 Add tests for HashSet, rapidhash withSeed consistency, and ReaddirCalc parity with calc 2026-04-12 23:44:23 -05:00
Antonio S. Musumeci a1e681f36b Use ReaddirCalc in all readdir implementations
* avoids per-entry path construction
2026-04-12 23:44:23 -05:00
Antonio S. Musumeci e3a467de80 Refactor fs_inode
* add Algo enum
* ReaddirCalc class
* string_view params
* fs::path overloads
2026-04-12 23:44:23 -05:00
Antonio S. Musumeci cdadda5eb9 Replace khash with custom hash set 2026-04-12 23:44:23 -05:00
Antonio SJ Musumeci 4c9a9d7e15 Upgrade rapidhash 2026-04-12 23:44:23 -05:00
Antonio SJ Musumeci 425e30b50f Use nanosleep rather than yield and return EINTR 2026-04-11 11:30:57 -05:00
Antonio SJ Musumeci 3905355522 Detect OS for release-sample make target 2026-04-08 20:48:02 -05:00
Antonio SJ Musumeci 9e2e1afafa Update docs on limiting drive spinup 2026-04-06 14:01:20 -05:00
Antonio SJ Musumeci 6d218fa87f Misc updates and fixes 2026-04-04 02:46:24 -05:00
Antonio SJ Musumeci 3b10406f95 Add more details to project comparisons 2026-04-04 02:17:06 -05:00
Antonio SJ Musumeci f2c8e5d9eb Add FUSE request_timeout
Not yet hooked up to config parser to set as need to evaluate usage.
2026-04-01 23:20:05 -05:00
Antonio SJ Musumeci b581c5d5e7 Add support for FUSE_COPY_FILE_RANGE_64 2026-04-01 23:20:05 -05:00
Antonio SJ Musumeci 1ebc8cfa5f Enable statx with file handle 2026-03-31 19:53:17 -04:00
Antonio SJ Musumeci acfa797503 Misc formatting changes 2026-03-31 19:53:17 -04:00
Antonio SJ Musumeci 7fc20457ca Rework open file resource management 2026-03-31 19:53:17 -04:00
Antonio SJ Musumeci ecee84b323 Improve proj comparisons + new SnapRAID details 2026-03-31 19:51:28 -04:00
Antonio SJ Musumeci b8776d205d Update docs; add nofs, policyfs and greyhole 2026-03-27 22:54:39 -05:00
Antonio SJ Musumeci 06433a0548 Fallback to fusermount3 where available
Also general cleanup of mount_generic.
2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci f4b602340b Add more integration tests 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci a78aef36e0 Additional check for ENOTCONN on mount 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 0d05523dfd Move maintainence thread func take u64 rather than int
signed rollover is UB
2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 0cfab60f37 replace raw C-strings with std::string in converted libfuse files
util/mount.mergerfs.cpp:
- Remove xstrdup/xrealloc wrappers entirely
- Convert type, source, options, command, setuid to std::string
- Replace add_arg() pointer-rewriting with shell_quote() returning std::string
- Replace add_option() with simple std::string += concatenation
- Fix const_cast UB on string literal assignment

lib/helper.cpp:
- Convert add_default_subtype local subtype_opt: malloc+snprintf+free -> std::string construction

lib/mount_generic.h:
- Convert fuse_mount_sys locals source/type: malloc+strcpy+strcat -> std::string
- Remove 4 unnecessary free() calls (RAII cleanup)
- .c_str() at mount() and fuse_mnt_add_mount() boundaries

lib/mount_util.h:
- Rewrite fuse_mnt_resolve_path internals using std::string: rfind/substr
  instead of manual pointer arithmetic, strrchr, strlen chains
2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 21966ade97 Convert vendored/libfuse C sources to C++17
Convert all C source files in vendored/libfuse/ to C++17:
- lib/crc32b.c    -> crc32b.cpp  (const void* cast)
- lib/fuse_opt.c -> fuse_opt.cpp (designated init, void* arithmetic)
- lib/fuse_signals.c -> fuse_signals.cpp
- lib/helper.c   -> helper.cpp  (void* data cast)
- lib/mount.c    -> mount.cpp  (thin wrapper)
- lib/mount_generic.c -> mount_generic.h (static inline, #pragma once)
- lib/mount_bsd.c -> mount_bsd.h    (static inline, #pragma once)
- lib/mount_util.c -> mount_util.h   (static inline, #pragma once)
- util/fusermount.c -> fusermount.cpp
- util/mount.mergerfs.c -> mount.mergerfs.cpp

Key changes:
- Replace C99 designated initializer in fuse_opt_parse with field-by-field init
- Fix void* pointer arithmetic via char* base + offset pattern
- All C-style (T*)voidptr casts replaced with static_cast/reinterpret_cast
- Remove redundant #define _GNU_SOURCE (already in FUSE_FLAGS)
- Wrap __clone2 declaration in extern "C" for ia64 path
- Update Makefile: empty SRC_C, build utils with $(CXX) instead of $(CC)
- Remove stale .c files now replaced by .cpp/.h counterparts

All binaries build cleanly: mergerfs, mergerfs-fusermount, mount.mergerfs
2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 96eb51eecb Move fuse_session to C++ and make exit flag atomic 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 881147daa2 Fix null ptr handling issue in ioctl processing 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 0f1a8e9b69 Fix error condition in fs_copyfile 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 08f9cc6932 Fix readlink implementation, add more tests 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 3099b15db1 More aggressive error handing with pagesize queries 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 07b1e6bdba Auto-commit fuse_msgbuf.cpp 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 7763c7384a Use fatal::abort() rather than assert in places 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 61365d226d Move scope_guard to vendored/ 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci f33e78bc24 Cleanup procfs and abort on critical issues 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 7f52623536 Cleanup branches and add more unit tests 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci a30a4df9c0 Cleanup config and add tests 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 1e0d5aa91d fs_wait_for_mount cleanup 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci d09fadbc28 fs_inode cleanup 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 21ce889449 Fixup listxattr error handling 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 9d1a4696e9 Small improvements to fs::statvfs_cache 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci ed0cbcf064 Cleanup fuse_readlink 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 076e359891 Improve thread pool 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci bf98d3f3a6 Fix possible error handling issue with errno 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci c45c5ce920 Make cfgfile depth calculation thread friendly 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci e0a892f0d0 Improve error handling when querying pagesize 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 164b7f73c9 Misc fixes and improvements to thread pool 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 3a45be4cd8 Rework string utils, more test cases 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 107a3acf95 Better error handling in pagesize config type 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci a5eafb8972 Check string is not empty before resize in Config::keys 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 8199cc8a8b Fix error handling in fuse chmod, chown, setxattr, removexattr 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci e5d897f3dc Ensure zero initialized timeouts structs 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci c0af09f7d8 Fix incorrect type in fs_read and write 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 1b1e14ca29 Remove dead branch in fuse_reply_data 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 82b89aa099 Change session->exited to sig_atomic_t 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci aa91e1b2ba Initialize stat buffer and add stat call in fuse_mount_sys 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 82e81afcdc Add NULL check for cmsg in receive_fd to prevent segfault 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 1a5c39f813 Fix buffer overflow in readlink by reserving space for null terminator 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci fce1803d42 Use RND class for shrink_to_rand_elem and add assertions 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 5a9a17afcb fix objpool: add size tracking, improve allocation matching; fix msgbuf: clear pool on size change 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 8648de6bb6 fix policy_eppfrd.cpp: return 0 when branchinfo found, -ENOENT only when empty 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci f227a63e15 fix str.cpp: use string_view::npos, fix erase() calls, remove extra whitespace 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci aa47b74dea Remove iterator methods from Branches class 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 206ad1dd53 Fix futimens bugs: stat pointer, variable names, and negation 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 80aead7085 Add null pointer checks for FileInfo/DirInfo 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 1220befacf Fix copydata_copy_file_range bug 2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 01aa888c7a Fix eplus search bug
Not that anyone would ever use this but it was noticed in a scan.
2026-03-24 21:13:34 -04:00
Antonio SJ Musumeci 6a6aa054cd Add support for Arch, btw 2026-03-14 11:39:48 -04:00
Antonio SJ Musumeci 04939bbe72 No longer explicitly error out on broken mount point
If ENOTCONN (broken FUSE mount) umount first and continue to mount.
2026-03-08 00:39:45 -05:00
Antonio SJ Musumeci 2ea6290e4b Update limiting drive spinup doc 2026-03-04 11:05:55 -05:00
Antonio SJ Musumeci c66c68b137 Misc cleanup 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 075a8a6548 Convert some push_back to emplace_back 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 3c6d1b47f6 Remove unused "using"s 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 6f281b9f7f Remove duplicate includes 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 511bf47af9 Convert stdint to short ints 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 53c5a2cf1e Move fmtlib to vendored and upgrade to 12.1.0 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci eb8074ed9a Update vendored/nonstd to latest and fix API usage 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 38c19df95a Update vendored/boost to 1.90.0 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci b1228f11bd Update vendored/acutest to latest master 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci cfafafd7ff Update vendored/CLI11 to v2.6.2 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci c8ce82ce54 Update vendored/subprocess to v2.5 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 05c3d97368 Update vendored/rapidhash to v3 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 2c87e68635 Cleanup ugid.hpp usage 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 8f1ef59770 Move 3rd party libraries to "vendored" directory 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci a460c52861 Remove unused files 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci a4aade6666 Make global inode calculation function pointer atomic 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 1d1bcd3b87 Add extra checks in for file handle lookup 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 099ce2d91d Add more asserts 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 56cd831ec8 Fix str::trim to handle all whitespace characters
Use std::isspace() instead of comparing only to space character.
Now handles tabs, newlines, carriage returns, etc.
2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci e43be3c221 Remove unused PolicyCache implementation 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 2754536727 Fix integer overflow in size suffix parsing
Use __builtin_mul_overflow to detect overflow when applying size
suffixes (K, M, G, T) in str::from for int64_t and uint64_t. Previously,
large values with suffixes could silently wrap around.
2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci a0cff637e1 Misc cleanup 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci a5a6c892b9 Update copyright headers 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 3d50d4a52f Explicitly return EXIT_FAILURE 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci f69c31fd77 Rework objpool 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci a2860c3f1f Use debug mutex more 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 3f70e66ee3 Rework node obj pool to use objpool 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 160d3c832d Fix branches unit test 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 15bcc47d6a Remove "channels" and embed into session 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci e55bb93890 Simplify lowlevel fuse state management and req mem 2026-03-02 01:09:16 -05:00
Antonio SJ Musumeci 5a2a9e4530 Remove fuse_ll_pipe dead code 2026-03-02 01:09:16 -05:00
trapexit b4239495a0 Update docs for logging changes + new video (#1618) 2026-02-12 23:21:43 -06:00
trapexit 3222de6c9c Fixup debug output (#1617) 2026-02-12 22:50:38 -06:00
Aaron Andersen be4d388813 Add support for -n to not update mtab (#1616) 2026-02-12 20:35:48 -06:00
trapexit a47842e2f8 Add MOS to projects and update misc links (#1614) 2026-01-27 11:25:32 -06:00
trapexit f769d7474b Add new links to media docs (#1613) 2026-01-27 07:59:38 -06:00
trapexit 2ae57642f4 Misc documentation updates (#1612) 2026-01-27 07:00:22 -06:00
trapexit 7f1b7ac343 Add lshw to collect info tool (#1610) 2026-01-19 13:57:42 -06:00
trapexit 1290ab9708 Ensure ::exists() call can't throw (#1609) 2026-01-15 17:33:31 -06:00
trapexit 965e26989c Misc documentation updates (#1605) 2025-12-23 23:25:39 -06:00
trapexit 0caae4fbf0 Tag container image to ghcr for convenience (#1604) 2025-12-14 15:21:56 -06:00
trapexit 4d1760cb69 Rework new lup policy to work on 32bit systems (#1603)
__int128 is not available on 32bit platforms. Moved to extended
precision floats.
2025-12-14 14:56:34 -06:00
François-Xavier Payet 22df87c86a Add LUP policy (#1601) 2025-12-14 00:36:12 -06:00
trapexit 72fb6e4aba Update submitting bug reports, improve collect-info (#1598) 2025-12-06 13:55:14 -06:00
trapexit 9b046d9a34 Rework credential handling to support chroot & idmap (#1595)
Run in an elevated credential mode (root) and let the kernel manage
entitlements. Will result in slightly different behavior but should
not be noticed by most.
2025-12-05 21:03:48 -06:00
Emanuel Haupt d8918458bf libfuse: fix narrowing conversion of nsec fields on i386 (#1593)
On 32 bit platforms tv_nsec is a signed long while the FUSE setattr
arguments provide nanoseconds as uint32_t. C++17 treats uint32_t -> long
in a brace initializer as a narrowing conversion and rejects it on ILP32
targets. This surfaced when building on FreeBSD i386 with clang 19 where
the compiler refused the list initialization of struct timespec.

Add explicit static_cast<long>() for the nsec fields to make the
conversion intentional and portable. This avoids build failures on i386
while leaving 64 bit builds unchanged.
2025-11-29 10:45:20 -06:00
trapexit d3a3f65e7b Ignore remote when forcing generation of docs (#1592) 2025-11-25 23:39:13 -06:00
trapexit 411d181473 Add qbittorrent specific section of known issues (#1591) 2025-11-25 23:14:20 -06:00
Emanuel Haupt caf7915003 tools/preload: include correct extended attribute headers on FreeBSD (#1590)
Add conditional includes for extended attribute support. FreeBSD uses
<sys/extattr.h> instead of <sys/xattr.h>, which prevents the preload
module from building. This update selects the proper header based on
the target platform and fixes the build failure on FreeBSD.
2025-11-25 12:09:41 -06:00
trapexit 4c5473d569 Remove cirris builds, codeql; redo mkdocs builds (#1585) 2025-11-19 10:28:38 -06:00
trapexit 104f378c2c Replace 'unknown' distro-codename when building deb from tarball src (#1584) 2025-11-19 07:57:58 -06:00
trapexit a364405cf0 Remove --depth=1 to clone for tarball builder (#1583)
Led to empty changelog
2025-11-19 06:56:23 -06:00
trapexit 960f5fdbba Fix some links in docs (#1582) 2025-11-18 08:35:27 -06:00
trapexit 6813a51fce Ensure cfg references do not also try to initialize the value (#1580) 2025-11-17 17:08:36 -06:00
trapexit f7e86d90ce Add FAQ about lack of create policies about file size (#1578) 2025-11-16 23:44:24 -06:00
trapexit 66d120c721 Add Ubuntu 25.10 build (#1577) 2025-11-16 22:30:32 -06:00
trapexit 01973214bf Slight tweaks to config parsing error handling (#1575) 2025-11-13 16:05:03 -06:00
trapexit af1edadf72 Fix size comparison for returning ERANGE in listxattr (#1574) 2025-11-12 18:58:50 -06:00
trapexit 3bacaa6f7f Add lazy-umount-mountpoint page and mount -a FAQ (#1573) 2025-11-12 12:19:00 -06:00
trapexit b6e1a08aef Add link to container image repo (#1570) 2025-11-11 19:38:18 -06:00
trapexit 47bd4259b8 Add container image details (#1569) 2025-11-08 20:28:10 -06:00
trapexit dc486e79ce Add container image building (#1568) 2025-11-06 23:39:05 -06:00
trapexit 78551049d5 Add ability to passthrough 'nofail' mount option (#1567) 2025-11-06 11:05:19 -06:00
trapexit 4aab008233 Ensure backward compatibility with remember & noforget (#1566) 2025-11-05 19:43:57 -06:00
trapexit 3a7a4a13e7 Improve mergerfs intro in docs (#1563) 2025-11-04 00:02:46 -06:00
trapexit 41b65ae9e8 Misc cleanup for FreeBSD and clang (#1560) 2025-11-02 10:49:22 -06:00
trapexit 2412ae7316 Further tweaks to config parsing and error reporting (#1559)
* Further tweaks to config parsing and error reporting

* Fix static builds
2025-11-01 15:20:30 -05:00
trapexit ec16d07fba Mention how mmap error when direct io is enabled (#1557) 2025-10-31 09:31:37 -05:00
trapexit 37f10888a9 Add more debugging options to makefiles and cleanup (#1556) 2025-10-29 22:42:42 -05:00
Satadru Pramanik, DO, MPH, MEng 0db8a1ed6a Fix manpage links for 2.41.0-rc3 (#1555) 2025-10-29 12:07:49 -05:00
trapexit cfaad60275 Misc updates to arg parsing (#1554)
Ignore "defaults" and add a number of more modern VFS mount switches
2025-10-26 21:05:05 -05:00
trapexit 077484323c Fix variable names of fuse-msg-size and posix-acl (#1553) 2025-10-25 17:00:16 -05:00
trapexit 700fcfccf5 Update fuse-msg-size docs (#1552) 2025-10-24 23:34:28 -05:00
trapexit 4b4801add6 Config options docs updates (#1551) 2025-10-24 22:31:04 -05:00
trapexit 741128cb23 Fix lack of include for fmt (#1550) 2025-10-24 21:00:15 -05:00
trapexit 3bee0f0b57 Misc docs updates (#1549) 2025-10-24 20:47:33 -05:00
trapexit 727c522fb8 Rework config, centralize fuse config (#1547) 2025-10-24 08:20:29 -05:00
trapexit 00e5467e99 Misc docs updates (#1544) 2025-10-13 11:44:53 -05:00
trapexit 0371f03def Rework how fuse request context is handled (#1543) 2025-10-12 23:21:36 -05:00
trapexit d18c273293 Allow setting of passthrough max-stack-depth (#1542) 2025-10-07 21:52:13 -05:00
trapexit 0f24bb29ac Make allow-idmap optional (default: false) (#1541)
This is just to correct default behavior for now. More investigation
is necessary to properly support idmapping.
2025-10-06 14:46:58 -05:00
trapexit 3cb35a3a65 Fix rename path generation (#1540) 2025-10-01 17:47:13 -05:00
trapexit 25b0bc72df Fix dirent64::namelen calculation (#1538)
Worked fine on my machine where padding seemed to be all zeroed out
but that doesn't appear to be the case elsewhere.

Could try
https://codeberg.org/jbruchon/libjodycode/src/branch/master/dir.c#L92-L110
but needs to be tested.
2025-09-30 11:25:19 -05:00
trapexit e6fcebe4f3 Misc build fixes (#1537) 2025-09-29 14:05:07 -05:00
trapexit 46f85c0f6d Misc fixes, mostly for FreeBSD (#1536) 2025-09-28 23:17:51 -05:00
trapexit 7a9c3b7eb2 Remove need for and use of ioprio header (#1535) 2025-09-28 00:01:26 -05:00
trapexit 569a151da2 Add ability to proxy ioprio of client apps (#1534) 2025-09-26 11:36:56 -05:00
trapexit 2f3e807e9d Add getdents based readdir functions for Linux (#1533) 2025-09-23 23:51:21 -05:00
trapexit 95ab37fdd2 Fix typo in visualization and add new vid to docs (#1532) 2025-09-22 16:23:50 -05:00
trapexit 1707dc27e3 Misc doc updates (#1531) 2025-09-21 14:25:24 -05:00
trapexit c4126a0796 Add script to generate docs from scratch and push to github (#1530) 2025-09-21 13:04:55 -05:00
pjv 6acef3a638 Fix typo in moveonenospc.md (#1527)
typo
2025-09-21 11:42:15 -05:00
trapexit b3656a0243 Fix broken doc links (#1525) 2025-09-19 19:25:31 -05:00
trapexit 41b40b94fa Misc docs updates (#1524) 2025-09-19 19:20:28 -05:00
trapexit ca59ae53a5 idmap mount support (#1523)
* Add idmap mount support

* Further replacement of std::filesystem::path to fs::path

* Update docs regarding idmap support
2025-09-18 00:07:28 -05:00
trapexit 8c649d33ee Start using std::filesystem::path for fusepath (#1522) 2025-09-17 22:51:16 -05:00
1033 changed files with 39036 additions and 28239 deletions
-150
View File
@@ -1,150 +0,0 @@
linux_task:
name: "alpine:latest"
container:
image: alpine:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "alpine-arm:latest"
arm_container:
image: alpine:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "fedora:41"
container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora-arm:41"
arm_container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "ubuntu:22.04"
container:
image: ubuntu:22.04
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu:24.04"
container:
image: ubuntu:24.04
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu:latest"
container:
image: ubuntu:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu-arm:latest"
arm_container:
image: ubuntu:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:12"
container:
image: debian:12
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:latest"
container:
image: debian:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian-arm:latest"
arm_container:
image: debian:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
+44 -16
View File
@@ -7,22 +7,31 @@ assignees: ''
---
See also https://trapexit.github.io/mergerfs/latest/support
**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. Not your distro's latest but the latest official release.**
**Please be sure to use latest release of mergerfs to ensure the issue
still exists. Not your distro's latest but the latest official
release. Support will NOT be provided for old versions.**
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.
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.
Provide steps to reproduce the behavior. List **all** steps to
reproduce. **All** settings. The version of all software involved.
Please simplify the reproduction as much as possible.
- Unless it is dependenat on multiple branches, use a single branch
- Reproduce with standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party software will make debugging more difficult.
* Unless it is dependenat on multiple branches, use a single branch
* Reproduce with standard tooling if possible
(touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party
software will make debugging more difficult.
**Expected behavior**
@@ -30,19 +39,38 @@ Please simplify the reproduction as much as possible.
A clear and concise description of the expected behavior.
**Workarounds already tested**
Please list all workarounds attempted and why they were ruled out as
options.
**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`
* Attach output from
[mergerfs.collect-info](https://trapexit.github.io/mergerfs/latest/tooling/#mergerfscollect-info)
tool.
* Alternatively:
* OS: `lsb_release -a`
* kernel version: `uname -a`
* mergerfs version: `mergerfs --version`
* mergerfs settings / arguments: from fstab, systemd unit, command
line, OMV plugin, etc.
* List of drives, filesystems, & sizes:
* `df -h`
* `lsblk -o NAME,FSTYPE,FSSIZE,SIZE,MOUNTPOINTS,RM,RO,ROTA`
* All information about the relevant paths and files: permissions,
ownership, etc.
* All information about the application making the requests:
version, uid/gid
* Is mergerfs running within a container or used with container
technologies?
* Are the client apps using mergerfs running in a container?
* 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**
-42
View File
@@ -1,42 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "7 3 * * 6"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python, cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
if: ${{ matrix.language == 'python' || matrix.language == 'cpp' }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
-49
View File
@@ -1,49 +0,0 @@
name: "mkdocs"
on:
push:
branches:
- master
tags:
- '*'
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Configure Git Credentials
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Install dependencies
working-directory: ./mkdocs
run: |
pip install --no-cache-dir mkdocs mkdocs-material pymdown-extensions mike
- name: Deploy docs
working-directory: ./mkdocs
env:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ "$REF_TYPE" = "tag" ]; then
mike deploy "$REF_NAME" "latest" --allow-empty --ignore-remote-status --update-aliases
mike set-default "latest"
#git checkout gh-pages
#git push -f origin gh-pages
elif [ "$REF_TYPE" = "branch" ] && [ "$REF_NAME" = "master" ]; then
mike deploy "master" --ignore-remote-status --allow-empty
#git checkout gh-pages
#git push -f origin gh-pages
fi
+1
View File
@@ -9,6 +9,7 @@
* scope_guard: https://github.com/Neargye/scope_guard
* subprocess: https://github.com/arun11299/cpp-subprocess
* CLI11: https://github.com/CLIUtils/CLI11
* string-lite: https://github.com/martinmoene/string-lite
* boost
* download boost
* ./bootstrap.sh
+1 -1
View File
@@ -1,7 +1,7 @@
/*
ISC License
Copyright (c) 2025, Antonio SJ Musumeci <trapexit@spawn.link>
Copyright (c) 2026, 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
+65 -19
View File
@@ -12,6 +12,8 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
$(info mergerfs MAKEFLAGS: $(MAKEFLAGS))
ifeq ($(shell id -u),0)
FAKEROOT ?=
endif
@@ -35,6 +37,8 @@ TOUCH ?= touch
BUILDDIR := build
GITHUB_REPO ?= "https://github.com/trapexit/mergerfs"
DEFAULT_RELEASE_SAMPLE := debian:13.amd64
ifndef GIT_REPO
ifneq ($(shell $(GIT) --version 2> /dev/null),)
ifeq ($(shell test -e .git; echo $$?),0)
@@ -49,7 +53,21 @@ UGID_USE_RWLOCK ?= 0
ifdef NDEBUG
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O0 -g -fno-omit-frame-pointer -DDEBUG
ifdef SANITIZE
ifeq ($(SANITIZE),1)
override SANITIZE := -fsanitize=address,undefined,leak
else ifeq ($(filter -fsanitize=%,$(SANITIZE)),)
override SANITIZE := -fsanitize=$(SANITIZE)
endif
endif
OPT_FLAGS := -O0 \
-g \
-fno-omit-frame-pointer \
$(SANITIZE) \
-fstack-protector-strong \
-Wextra \
-Wno-unused-parameter \
-DDEBUG
endif
ifdef STATIC
@@ -69,7 +87,7 @@ OBJS := $(SRC:src/%.cpp=build/.objs/%.cpp.o)
DEPS := $(SRC:src/%.cpp=build/.objs/%.cpp.d)
TESTS := $(wildcard tests/*.cpp)
TESTS_OBJS := $(filter-out build/.objs/mergerfs.o,$(OBJS))
TESTS_OBJS := $(filter-out build/.objs/mergerfs.cpp.o,$(OBJS))
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.o)
TESTS_DEPS := $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.d)
TESTS_DEPS += $(DEPS)
@@ -77,7 +95,8 @@ TESTS_DEPS += $(DEPS)
MANPAGE := mergerfs.1
CPPFLAGS ?=
override CPPFLAGS += \
-D_FILE_OFFSET_BITS=64
-D_FILE_OFFSET_BITS=64 \
-DBOOST_UNORDERED_DISABLE_PARALLEL_ALGORITHMS
CFLAGS ?= \
$(OPT_FLAGS) \
-Wall \
@@ -103,23 +122,30 @@ override CXXFLAGS += \
-MP
override INC_FLAGS := \
-Isrc \
-Ilibfuse/include
-Ivendored \
-Ivendored/libfuse/include
override MFS_FLAGS := \
-DUSE_XATTR=$(USE_XATTR) \
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
override TESTS_FLAGS := \
-Isrc \
-Ivendored \
-Ivendored/acutest \
-DTESTS
LIBFUSE := libfuse/$(BUILDDIR)/libfuse.a
LIBFUSE := vendored/libfuse/$(BUILDDIR)/libfuse.a
LDFLAGS ?=
LDLIBS := \
-lrt \
-latomic \
-pthread \
-lstdc++fs
-pthread
override LDLIBS += \
$(LIBFUSE)
ifeq ($(CXX),g++)
GCC_VERSION := $(shell $(CXX) -dumpversion | cut -f1 -d.)
ifeq ($(shell test $(GCC_VERSION) -lt 9; echo $$?),0)
override LDLIBS += -lstdc++fs
endif
endif
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
DESTDIR ?=
@@ -148,6 +174,8 @@ help:
@echo "USE_XATTR=0 - build program without xattrs functionality"
@echo "STATIC=1 - build static binary"
@echo "LTO=1 - build with link time optimization"
@echo "SANITIZE=1 - build with sanitizers (address,undefined,leak)"
@echo "SANITIZE=list - build with custom sanitizers (comma separated)"
@echo "CLEANUP=1 - cleanup images between release build process"
@echo "PKGDIR=/dir/ - location for release build pkgs"
@echo "GITREF=gitref - gitref to use for release builds"
@@ -156,19 +184,21 @@ help:
$(BUILDDIR)/mergerfs: $(LIBFUSE) src/version.hpp $(OBJS)
$(CXX) $(CXXFLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
$(BUILDDIR)/fsck.mergerfs:
$(BUILDDIR)/fsck.mergerfs: $(BUILDDIR)/mergerfs
$(LN) -sf "mergerfs" $@
$(BUILDDIR)/mergerfs.collect-info:
$(BUILDDIR)/mergerfs.collect-info: $(BUILDDIR)/mergerfs
$(LN) -sf "mergerfs" $@
$(BUILDDIR)/tests: $(BUILDDIR)/mergerfs $(TESTS_OBJS)
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ $(LDFLAGS) $(LDLIBS)
.PHONY: libfuse
libfuse: $(LIBFUSE)
$(MAKE) -C vendored/libfuse
$(LIBFUSE):
libfuse:
$(MAKE) NDEBUG=$(NDEBUG) -C libfuse
$(MAKE) -C vendored/libfuse $(BUILDDIR)/libfuse.a
tests: $(BUILDDIR)/tests
@@ -194,7 +224,7 @@ $(BUILDDIR)/stamp:
$(BUILDDIR)/.objs/%.cpp.o: src/%.cpp $(BUILDDIR)/stamp
$(CXX) $(CXXFLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/.test_objs/%.cpp.o: tests/%.cpp
$(BUILDDIR)/.test_objs/%.cpp.o: tests/%.cpp | $(BUILDDIR)/stamp
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(INC_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
$(BUILDDIR)/preload.so: $(BUILDDIR)/stamp tools/preload.c
@@ -206,7 +236,7 @@ preload: $(BUILDDIR)/preload.so
clean: rpm-clean
$(RM) -rf $(BUILDDIR)
$(FIND) . -name "*~" -delete
$(MAKE) -C libfuse clean
$(MAKE) -C vendored/libfuse clean
.PHONY: distclean
distclean: clean
@@ -227,7 +257,7 @@ install-base: all
.PHONY: install-mount-tools
install-mount-tools: install-base
$(MKDIR) -p "$(INSTALLBINDIR)"
$(MAKE) -C libfuse install
$(MAKE) -C vendored/libfuse install
.PHONY: install-man
install-man: man/$(MANPAGE)
@@ -244,10 +274,12 @@ install-strip: install-base
$(STRIP) "$(INSTALLBINDIR)/mergerfs"
.PHONY: uninstall
uninstall: uninstall-base uninstall-mount.mergerfs uninstall-man
uninstall: uninstall-base uninstall-mount.mergerfs uninstall-preload uninstall-man
uninstall-base:
$(RM) -f "$(INSTALLBINDIR)/mergerfs"
$(RM) -f "$(INSTALLBINDIR)/fsck.mergerfs"
$(RM) -f "$(INSTALLBINDIR)/mergerfs.collect-info"
uninstall-mount.mergerfs:
$(RM) -f "$(INSTALLBINDIR)/mount.mergerfs"
@@ -255,6 +287,9 @@ uninstall-mount.mergerfs:
uninstall-man:
$(RM) -f "$(INSTALLMAN1DIR)/$(MANPAGE)"
uninstall-preload:
$(RM) -f "$(INSTALLLIBDIR)/preload.so"
.PHONY: tarball
tarball: changelog version
$(eval VERSION := $(shell cat VERSION))
@@ -272,6 +307,10 @@ ifdef GIT_REPO
$(GIT2DEBCL) --name mergerfs > debian/changelog
else
$(CP) -v ChangeLog debian/changelog
$(eval DISTRO := $(shell lsb_release -i -s | tr '[:upper:]' '[:lower:]'))
$(eval CODENAME := $(shell lsb_release -c -s | tr '[:upper:]' '[:lower:]'))
sed -i 's/~unknown-unknown/~$(DISTRO)-$(CODENAME)/g' debian/changelog
sed -i 's/ unknown; / $(CODENAME); /g' debian/changelog
endif
.PHONY: signed-deb
@@ -310,10 +349,11 @@ install-build-pkgs:
.PHONY: install-build-tools
install-build-tools:
./bulidtools/install-build-tools
./buildtools/install-build-tools
define build_release
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
@echo "GITREF=$(GITREF)"
./buildtools/build-release \
--target=$(1) \
$(if $(CLEANUP),--cleanup) \
@@ -328,7 +368,8 @@ endef
release:
$(call build_release,"all")
release-sample:
$(call build_release,"debian:13.amd64")
$(if $(RELEASE_SAMPLE),,$(eval RELEASE_SAMPLE := $(shell ./buildtools/detect-local-target 2>/dev/null)))
$(call build_release,$(if $(RELEASE_SAMPLE),$(RELEASE_SAMPLE),$(DEFAULT_RELEASE_SAMPLE)))
release-amd64:
$(call build_release,"amd64")
release-arm64:
@@ -336,12 +377,17 @@ release-arm64:
release-armhf:
$(call build_release,"armhf")
release-riscv64:
$(call build_release,"risc64")
$(call build_release,"riscv64")
release-static:
$(call build_release,"static")
release-tarball:
$(call build_release,"tarball")
container:
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
@echo "GITREF=$(GITREF)"
./buildtools/build-containerimage "$(GITREF)"
.PHONY: tags
tags:
rm -fv TAGS
+6 -3
View File
@@ -25,6 +25,7 @@ etc.](https://trapexit.github.io/mergerfs/latest/project_comparisons/)
* Works with filesystems of [almost any
type](https://trapexit.github.io/mergerfs/latest/faq/compatibility_and_integration/#what-filesystems-can-be-used-as-branches)
* Ignore read-only filesystems when creating files
* [Hard links](https://trapexit.github.io/mergerfs/latest/faq/technical_behavior_and_limitations/#do-hard-links-work)
* Hard link [copy-on-write /
CoW](https://trapexit.github.io/mergerfs/latest/config/link_cow/)
* [Runtime configurable](https://trapexit.github.io/mergerfs/latest/runtime_interface/)
@@ -59,8 +60,10 @@ which branch will be selected for the creation.
For functions which change attributes or remove the file the behavior
may be applied to all instances found.
Read more about [policies
here](https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies/).
The way in which mergerfs behaves is controlled by the
[config/options/settings](https://trapexit.github.io/mergerfs/latest/config/options). More
specifically by
[policies](https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies/).
### Visualization
@@ -77,7 +80,7 @@ A + B = C
| | |
+-- /dir2 | +-- /dir2
| | | | |
| *-- file4 | | +-- file4
| +-- file4 | | +-- file4
| | |
| +-- /dir3 +-- /dir3
| | | | |
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
GIT_BRANCH="${1:-master}"
set -x
podman \
build \
--squash \
--no-cache \
--force-rm \
--build-arg="GIT_REPO=file:///mnt" \
--build-arg="BRANCH=${GIT_BRANCH}" \
-v "${PWD}":/mnt:ro \
-f "buildtools/containerimage/Containerfile" \
--tag "ghcr.io/trapexit/mergerfs:${GIT_BRANCH}" \
buildtools/
+24 -23
View File
@@ -27,37 +27,38 @@ git log HEAD^1..
mkdir "/build"
if [ -e /usr/bin/apt-get ]; then
make deb
find /tmp/ \
-type f \
-name "*.deb" \
-not -name "*dbgsym*" \
-exec cp -v {} /build/ \;
exit 0
elif [ -e /usr/bin/dnf ]; then
make rpm
find /tmp/ \
-type f \
-name "*.rpm" \
-not -name "*sym*" \
-not -name "*src.rpm" \
-exec cp -v {} /build/ \;
exit 0
elif [ -e /usr/bin/yum ]; then
. /opt/rh/devtoolset-9/enable
make rpm
find /tmp/ \
elif [ -e /sbin/apk ]; then
echo "NOT YET SUPPORTED"
exit 1
elif [ -e /usr/sbin/pkg ]; then
echo "NOT YET SUPPORTED"
exit 1
elif [ -e /usr/bin/pacman ]; then
make tarball
else
echo "NOT YET SUPPORTED"
exit 1
fi
echo "Copy packages to host..."
if [ -e /usr/bin/pacman ]; then
find "${BUILDDIR}" \
-type f \
-name "*.rpm" \
-name "*.tar.gz" \
-exec cp -v {} /build/ \;
else
find "${BUILDDIR}" \
-type f \
\( -name "*.deb" -or -name "*.rpm" \) \
-not -name "*dbgsym*" \
-not -name "*sym*" \
-not -name "*src.rpm" \
-exec cp -v {} /build/ \;
exit 0
elif [ -e /sbin/apk ]; then
echo "NOT YET SUPPORTED"
elif [ -e /usr/sbin/pkg ]; then
echo "NOT YET SUPPORTED"
else
echo "NOT YET SUPPORTED"
fi
exit 1
exit 0
+18 -7
View File
@@ -19,7 +19,7 @@ def build(git_repo,
if os.path.exists(git_repo):
git_repo = os.path.realpath(git_repo)
args += ['-v',f'{git_repo}:/tmp/mergerfs.git:ro']
args += ['--build-arg=GIT_REPO=/tmp/mergerfs.git']
args += ['--build-arg=GIT_REPO=file:///tmp/mergerfs.git']
else:
args += [f'--build-arg=GIT_REPO={git_repo}']
args += ['-o',pkgdirpath,
@@ -47,12 +47,23 @@ def build(git_repo,
def setup():
args = ['sudo',
'apt-get',
'install',
'-fy',
'qemu-user-static',
'qemu-user-binfmt']
# Detect distribution
if os.path.exists('/usr/bin/apt-get'):
args = ['sudo',
'apt-get',
'install',
'-fy',
'qemu-user-static',
'qemu-user-binfmt']
elif os.path.exists('/usr/bin/pacman'):
args = ['sudo',
'pacman',
'-Sy',
'--noconfirm',
'qemu-user-static']
else:
print("Unsupported distribution for setup")
sys.exit(1)
print(args)
subprocess.run(args)
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/amd64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_amd64.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_amd64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/arm64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_arm64.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_arm64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/armhf alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_armhf.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_armhf.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/i386 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_i386.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_i386.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/riscv64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_riscv64.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_riscv64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+7 -3
View File
@@ -1,13 +1,17 @@
ARG BUILD_TIMESTAMP=0
FROM alpine:latest as tarball
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG GIT_REPO
RUN git clone --single-branch "${GIT_REPO}" /tmp/mergerfs --branch="${BRANCH}"
RUN cd /tmp/mergerfs && make version tarball
RUN mkdir /build
RUN cp -v /tmp/mergerfs/*.tar.gz /build/
FROM scratch
COPY --from=tarball /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+19
View File
@@ -0,0 +1,19 @@
FROM --platform=linux/amd64 alpine:latest as build
ARG GIT_REPO
ARG BRANCH
RUN <<EOF
apk add git gcc g++ make linux-headers
git clone --single-branch --branch="${BRANCH}" "${GIT_REPO}" /tmp/mergerfs
cd /tmp/mergerfs
make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp/install -j$(nproc) install
tar cvfz /tmp/mergerfs.tgz --directory=/tmp/install usr sbin
EOF
FROM --platform=linux/amd64 alpine:latest
RUN --mount=from=build,source=/tmp/mergerfs.tgz,target=/tmp/mergerfs.tgz <<EOF
tar xvf /tmp/mergerfs.tgz -C /
EOF
ENTRYPOINT ["/usr/local/bin/mergerfs"]
CMD ["--help"]
+119
View File
@@ -0,0 +1,119 @@
#!/bin/bash
# Detects local OS and maps to containerfiles target
# Returns target if found in buildtools/containerfiles/, empty string otherwise
set -euo pipefail
CONTAINERFILES_DIR="buildtools/containerfiles"
# Detect system architecture and map to containerfiles format
detect_arch() {
local arch
arch=$(uname -m)
case "$arch" in
x86_64) echo "amd64" ;;
aarch64) echo "arm64" ;;
armv7l) echo "armhf" ;;
i686|i386) echo "i386" ;;
riscv64) echo "riscv64" ;;
*) echo "" ;;
esac
}
# Detect distro and version from os-release
detect_distro() {
local os_release="${1:-}"
if [[ ! -r "$os_release" ]]; then
return 1
fi
# Source the file safely - ID and VERSION_ID are environment variables
. "$os_release"
local distro="$ID"
local version="$VERSION_ID"
# Map distro ID to containerfiles naming convention
case "$distro" in
ubuntu|debian|fedora) ;;
rocky|rhel|centos|almalinux) distro="rockylinux" ;;
*)
# Fallback to ID_LIKE for derivatives
if [[ -n "${ID_LIKE:-}" ]]; then
case "$ID_LIKE" in
*ubuntu*) distro="ubuntu" ;;
*debian*) distro="debian" ;;
*fedora*) distro="fedora" ;;
esac
fi
;;
esac
# Only return if we have valid distro and version
if [[ -n "$distro" && -n "$version" ]]; then
echo "$distro:$version"
else
return 1
fi
}
# Try to find a matching containerfile target
find_containerfile() {
local target="$1"
local containerfile="${CONTAINERFILES_DIR}/${target}"
# Direct match
if [[ -d "$containerfile" || -f "$containerfile" ]]; then
echo "$target"
return 0
fi
return 1
}
# Main detection logic
main() {
local arch
# Detect architecture
arch=$(detect_arch)
if [[ -z "$arch" ]]; then
echo "WARNING: Could not detect system architecture" >&2
exit 1
fi
# Try to detect distro from multiple locations
local distro_ver
distro_ver=$(detect_distro "/etc/os-release") || \
distro_ver=$(detect_distro "/usr/lib/os-release") || {
echo "WARNING: Could not detect distro from os-release" >&2
exit 1
}
# Build target string
local target="${distro_ver}.${arch}"
# Try to find matching containerfile
if find_containerfile "$target"; then
exit 0
fi
# Try with partial version matching (e.g., 25.10 → 25)
local distro="${distro_ver%%:*}"
local version="${distro_ver##*:}"
# Strip last minor version number and try again
local short_version="${version%.*}"
if [[ -n "$short_version" ]]; then
target="${distro}:${short_version}.${arch}"
if find_containerfile "$target"; then
exit 0
fi
fi
# No match found
echo "WARNING: No containerfile found for target: ${distro_ver}.${arch}" >&2
exit 1
}
main "$@"
+2
View File
@@ -34,4 +34,6 @@ elif [ -e /sbin/apk ]; then
elif [ -e /usr/sbin/pkg ]; then
pkg install \
git gmake gcc
elif [ -e /usr/bin/pacman ]; then
pacman -Sy --noconfirm base-devel git fakeroot
fi
+18 -5
View File
@@ -1,19 +1,32 @@
#!/bin/sh
if [ "$(id -u)" -eq 0 ]; then
SUDO=
elif command -v sudo >/dev/null 2>&1; then
SUDO=sudo
elif command -v doas >/dev/null 2>&1; then
SUDO=doas
else
printf '%s\n' "error: need root privileges; run as root or install sudo/doas" >&2
exit 1
fi
if [ -e /usr/bin/apt-get ]; then
export DEBIAN_FRONTEND=noninteractive
apt-get -qy update
apt-get -qy --force-yes --no-install-suggests --no-install-recommends install \
${SUDO} apt-get -qy update
${SUDO} apt-get -qy --no-install-suggests --no-install-recommends install \
qemu-user-static \
podman
elif [ -e /usr/bin/dnf ]; then
dnf -y update
dnf -y install \
${SUDO} dnf -y update
${SUDO} dnf -y install \
qemu-user-static \
podman
elif [ -e /sbin/apk ]; then
apk add \
${SUDO} apk add \
python3 \
qemu-user-static \
podman
elif [ -e /usr/bin/pacman ]; then
${SUDO} pacman -Sy --noconfirm qemu-user-static podman
fi
-786
View File
@@ -1,786 +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.
*/
#ifndef _FUSE_H_
#define _FUSE_H_
#include "extern_c.h"
#include "fuse_common.h"
#include "fuse_kernel.h"
#include <fcntl.h>
#include <time.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/uio.h>
#include <stdint.h>
EXTERN_C_BEGIN
/* ----------------------------------------------------------- *
* Basic FUSE API *
* ----------------------------------------------------------- */
/** Handle for a FUSE filesystem */
struct fuse;
/** Structure containing a raw command */
struct fuse_cmd;
struct fuse_dirents_t;
typedef struct fuse_dirents_t fuse_dirents_t;
/**
* The file system operations:
*
* Most of these should work very similarly to the well known UNIX
* file system operations. A major exception is that instead of
* returning an error in 'errno', the operation should return the
* negated error value (-errno) directly.
*
* All methods are optional, but some are essential for a useful
* filesystem (e.g. getattr). Open, flush, release, fsync, opendir,
* releasedir, fsyncdir, access, create, ftruncate, fgetattr, lock,
* init and destroy are special purpose methods, without which a full
* featured filesystem can still be implemented.
*
* Almost all operations take a path which can be of any length.
*
* Changed in fuse 2.8.0 (regardless of API version)
* Previously, paths were limited to a length of PATH_MAX.
*
* See http://fuse.sourceforge.net/wiki/ for more information. There
* is also a snapshot of the relevant wiki pages in the doc/ folder.
*/
struct fuse_operations
{
/** Get file attributes.
*
* Similar to stat(). The 'st_dev' and 'st_blksize' fields are
* ignored. The 'st_ino' field is ignored except if the 'use_ino'
* mount option is given.
*/
int (*getattr) (const char *, struct stat *, fuse_timeouts_t *);
/** Read the target of a symbolic link
*
* The buffer should be filled with a null terminated string. The
* buffer size argument includes the space for the terminating
* null character. If the linkname is too long to fit in the
* buffer, it should be truncated. The return value should be 0
* for success.
*/
int (*readlink) (const char *, char *, size_t);
/** Create a file node
*
* This is called for creation of all non-directory, non-symlink
* nodes. If the filesystem defines a create() method, then for
* regular files that will be called instead.
*/
int (*mknod) (const char *, mode_t, dev_t);
/** Create a directory
*
* Note that the mode argument may not have the type specification
* bits set, i.e. S_ISDIR(mode) can be false. To obtain the
* correct directory type bits use mode|S_IFDIR
* */
int (*mkdir) (const char *, mode_t);
/** Remove a file */
int (*unlink) (const char *);
/** Remove a directory */
int (*rmdir) (const char *);
/** Create a symbolic link */
int (*symlink) (const char *, const char *, struct stat *, fuse_timeouts_t *);
/** Rename a file */
int (*rename) (const char *, const char *);
/** Create a hard link to a file */
int (*link) (const char *, const char *, struct stat *, fuse_timeouts_t *);
/** Change the permission bits of a file */
int (*chmod) (const char *, mode_t);
int (*fchmod)(const uint64_t, const mode_t);
/** Change the owner and group of a file */
int (*chown) (const char *, uid_t, gid_t);
int (*fchown)(const uint64_t, const uid_t, const gid_t);
/** Change the size of a file */
int (*truncate) (const char *, off_t);
/** File open operation
*
* No creation (O_CREAT, O_EXCL) and by default also no
* truncation (O_TRUNC) flags will be passed to open(). If an
* application specifies O_TRUNC, fuse first calls truncate()
* and then open(). Only if 'atomic_o_trunc' has been
* specified and kernel version is 2.6.24 or later, O_TRUNC is
* passed on to open.
*
* Unless the 'default_permissions' mount option is given,
* open should check if the operation is permitted for the
* given flags. Optionally open may also return an arbitrary
* filehandle in the fuse_file_info structure, which will be
* passed to all file operations.
*
* Changed in version 2.2
*/
int (*open) (const char *, fuse_file_info_t *);
/** Get file system statistics
*
* The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
*
* Replaced 'struct statfs' parameter with 'struct statvfs' in
* version 2.5
*/
int (*statfs) (const char *, struct statvfs *);
/** Possibly flush cached data
*
* BIG NOTE: This is not equivalent to fsync(). It's not a
* request to sync dirty data.
*
* Flush is called on each close() of a file descriptor. So if a
* filesystem wants to return write errors in close() and the file
* has cached dirty data, this is a good place to write back data
* and return any errors. Since many applications ignore close()
* errors this is not always useful.
*
* NOTE: The flush() method may be called more than once for each
* open(). This happens if more than one file descriptor refers
* to an opened file due to dup(), dup2() or fork() calls. It is
* not possible to determine if a flush is final, so each flush
* should be treated equally. Multiple write-flush sequences are
* relatively rare, so this shouldn't be a problem.
*
* Filesystems shouldn't assume that flush will always be called
* after some writes, or that if will be called at all.
*
* Changed in version 2.2
*/
int (*flush) (const fuse_file_info_t *);
/** Release an open file
*
* Release is called when there are no more references to an open
* file: all file descriptors are closed and all memory mappings
* are unmapped.
*
* For every open() call there will be exactly one release() call
* with the same flags and file descriptor. It is possible to
* have a file opened more than once, in which case only the last
* release will mean, that no more reads/writes will happen on the
* file. The return value of release is ignored.
*
* Changed in version 2.2
*/
int (*release) (const fuse_file_info_t *);
/** Synchronize file contents
*
* If the datasync parameter is non-zero, then only the user data
* should be flushed, not the meta data.
*
* Changed in version 2.2
*/
int (*fsync) (const uint64_t, int);
/** Set extended attributes */
int (*setxattr) (const char *, const char *, const char *, size_t, int);
/** Get extended attributes */
int (*getxattr) (const char *, const char *, char *, size_t);
/** List extended attributes */
int (*listxattr) (const char *, char *, size_t);
/** Remove extended attributes */
int (*removexattr) (const char *, const char *);
/** Open directory
*
* Unless the 'default_permissions' mount option is given,
* this method should check if opendir is permitted for this
* directory. Optionally opendir may also return an arbitrary
* filehandle in the fuse_file_info structure, which will be
* passed to readdir, closedir and fsyncdir.
*
* Introduced in version 2.3
*/
int (*opendir) (const char *,
fuse_file_info_t *);
/** Read directory
*
* This supersedes the old getdir() interface. New applications
* should use this.
*
* The filesystem may choose between two modes of operation:
*
* 1) The readdir implementation ignores the offset parameter, and
* passes zero to the filler function's offset. The filler
* function will not return '1' (unless an error happens), so the
* whole directory is read in a single readdir operation. This
* works just like the old getdir() method.
*
* 2) The readdir implementation keeps track of the offsets of the
* directory entries. It uses the offset parameter and always
* passes non-zero offset to the filler function. When the buffer
* is full (or an error happens) the filler function will return
* '1'.
*
* Introduced in version 2.3
*/
int (*readdir)(const fuse_file_info_t *,
fuse_dirents_t *);
int (*readdir_plus)(const fuse_file_info_t *,
fuse_dirents_t *);
/** Release directory
*
* Introduced in version 2.3
*/
int (*releasedir) (const fuse_file_info_t *);
/** Synchronize directory contents
*
* If the datasync parameter is non-zero, then only the user data
* should be flushed, not the meta data
*
* Introduced in version 2.3
*/
int (*fsyncdir) (const fuse_file_info_t *, int);
/**
* Initialize filesystem
*
* The return value will passed in the private_data field of
* fuse_context to all file operations and as a parameter to the
* destroy() method.
*
* Introduced in version 2.3
* Changed in version 2.6
*/
void *(*init) (struct fuse_conn_info *conn);
/**
* Clean up filesystem
*
* Called on filesystem exit.
*
* Introduced in version 2.3
*/
void (*destroy) (void);
/**
* Check file access permissions
*
* This will be called for the access() system call. If the
* 'default_permissions' mount option is given, this method is not
* called.
*
* This method is not called under Linux kernel versions 2.4.x
*
* Introduced in version 2.5
*/
int (*access) (const char *, int);
/**
* Create and open a file
*
* If the file does not exist, first create it with the specified
* mode, and then open it.
*
* If this method is not implemented or under Linux kernel
* versions earlier than 2.6.15, the mknod() and open() methods
* will be called instead.
*
* Introduced in version 2.5
*/
int (*create) (const char *, mode_t, fuse_file_info_t *);
/**
* Change the size of an open file
*
* This method is called instead of the truncate() method if the
* truncation was invoked from an ftruncate() system call.
*
* If this method is not implemented or under Linux kernel
* versions earlier than 2.6.15, the truncate() method will be
* called instead.
*
* Introduced in version 2.5
*/
int (*ftruncate) (const uint64_t, off_t);
/**
* Get attributes from an open file
*
* This method is called instead of the getattr() method if the
* file information is available.
*
* Currently this is only called after the create() method if that
* is implemented (see above). Later it may be called for
* invocations of fstat() too.
*
* Introduced in version 2.5
*/
int (*fgetattr) (const uint64_t, struct stat *, fuse_timeouts_t *);
/**
* Perform POSIX file locking operation
*
* The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
*
* For the meaning of fields in 'struct flock' see the man page
* for fcntl(2). The l_whence field will always be set to
* SEEK_SET.
*
* For checking lock ownership, the 'fuse_file_info->owner'
* argument must be used.
*
* For F_GETLK operation, the library will first check currently
* held locks, and if a conflicting lock is found it will return
* information without calling this method. This ensures, that
* for local locks the l_pid field is correctly filled in. The
* results may not be accurate in case of race conditions and in
* the presence of hard links, but it's unlikely that an
* application would rely on accurate GETLK results in these
* cases. If a conflicting lock is not found, this method will be
* called, and the filesystem may fill out l_pid by a meaningful
* value, or it may leave this field zero.
*
* For F_SETLK and F_SETLKW the l_pid field will be set to the pid
* of the process performing the locking operation.
*
* Note: if this method is not implemented, the kernel will still
* allow file locking to work locally. Hence it is only
* interesting for network filesystems and similar.
*
* Introduced in version 2.6
*/
int (*lock) (const fuse_file_info_t *,
int cmd,
struct flock *);
/**
* Change the access and modification times of a file with
* nanosecond resolution
*
* This supersedes the old utime() interface. New applications
* should use this.
*
* See the utimensat(2) man page for details.
*
* Introduced in version 2.6
*/
int (*utimens)(const char *, const struct timespec tv[2]);
int (*futimens)(const uint64_t fh, const struct timespec tv[2]);
/**
* Map block index within file to block index within device
*
* Note: This makes sense only for block device backed filesystems
* mounted with the 'blkdev' option
*
* Introduced in version 2.6
*/
int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
/**
* Ioctl
*
* flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
* 64bit environment. The size and direction of data is
* determined by _IOC_*() decoding of cmd. For _IOC_NONE,
* data will be NULL, for _IOC_WRITE data is out area, for
* _IOC_READ in area and if both are set in/out area. In all
* non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
*
* If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a
* directory file handle.
*
* Introduced in version 2.8
*/
int (*ioctl) (const fuse_file_info_t *ffi,
unsigned long cmd,
void *arg,
unsigned int flags,
void *data,
uint32_t *out_bufsz);
/**
* Poll for IO readiness events
*
* Note: If ph is non-NULL, the client should notify
* when IO readiness events occur by calling
* fuse_notify_poll() with the specified ph.
*
* Regardless of the number of times poll with a non-NULL ph
* is received, single notification is enough to clear all.
* Notifying more times incurs overhead but doesn't harm
* correctness.
*
* The callee is responsible for destroying ph with
* fuse_pollhandle_destroy() when no longer in use.
*
* Introduced in version 2.8
*/
int (*poll) (const fuse_file_info_t *ffi,
fuse_pollhandle_t *ph,
unsigned *reventsp);
/** Write contents of buffer to an open file
*
* Similar to the write() method, but data is supplied in a
* generic buffer. Use fuse_buf_copy() to transfer data to
* the destination.
*
* Introduced in version 2.9
*/
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
*
* Similar to the read() method, but data is stored and
* returned in a generic buffer.
*
* No actual copying of data has to take place, the source
* file descriptor may simply be stored in the buffer for
* later data transfer.
*
* The buffer must be allocated dynamically and stored at the
* location pointed to by bufp. If the buffer contains memory
* regions, they too must be allocated using malloc(). The
* allocated memory will be freed by the caller.
*
* Introduced in version 2.9
*/
int (*read)(const fuse_file_info_t *ffi,
char *buf,
size_t size,
off_t off);
/**
* Perform BSD file locking operation
*
* The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
*
* Nonblocking requests will be indicated by ORing LOCK_NB to
* the above operations
*
* For more information see the flock(2) manual page.
*
* Additionally fi->owner will be set to a value unique to
* this open file. This same value will be supplied to
* ->release() when the file is released.
*
* Note: if this method is not implemented, the kernel will still
* allow file locking to work locally. Hence it is only
* interesting for network filesystems and similar.
*
* Introduced in version 2.9
*/
int (*flock) (const fuse_file_info_t *, int op);
/**
* Allocates space for an open file
*
* This function ensures that required space is allocated for specified
* file. If this function returns success then any subsequent write
* request to specified range is guaranteed not to fail because of lack
* of space on the file system media.
*
* Introduced in version 2.9.1
*/
int (*fallocate) (const uint64_t, int, off_t, off_t);
/**
* Copy a range of data from one file to another
*
* Performs an optimized copy between two file descriptors without
* the additional cost of transferring data through the FUSE kernel
* module to user space (glibc) and then back into the FUSE filesystem
* again.
*
* In case this method is not implemented, glibc falls back to
* reading data from the source and writing to the
* destination. Effectively doing an inefficient copy of the
* data.
*/
ssize_t (*copy_file_range)(const fuse_file_info_t *fi_in,
off_t offset_in,
const fuse_file_info_t *fi_out,
off_t offset_out,
size_t size,
int flags);
ssize_t (*setupmapping)(uint64_t *fh_,
uint64_t foffset_,
uint64_t len_,
uint64_t flags_,
uint64_t moffset_);
int (*removemapping)();
int (*syncfs)();
int (*tmpfile)(const char *, mode_t, fuse_file_info_t *);
int (*statx)(const char *fusepath,
const uint32_t flags,
const uint32_t mask,
struct fuse_statx *st,
fuse_timeouts_t *timeout);
int (*statx_fh)(const uint64_t fh,
const uint32_t flags,
const uint32_t mask,
struct fuse_statx *st,
fuse_timeouts_t *timeout);
};
/** Extra context that may be needed by some filesystems
*
* The uid, gid and pid fields are not filled in case of a writepage
* operation.
*/
struct fuse_context
{
uint64_t unique;
uint64_t nodeid;
uint32_t opcode;
uid_t uid;
gid_t gid;
pid_t pid;
mode_t umask;
struct fuse *fuse;
};
/**
* Main function of FUSE.
*
* This is for the lazy. This is all that has to be called from the
* main() function.
*
* This function does the following:
* - parses command line options (-d -s and -h)
* - passes relevant mount options to the fuse_mount()
* - installs signal handlers for INT, HUP, TERM and PIPE
* - registers an exit handler to unmount the filesystem on program exit
* - creates a fuse handle
* - registers the operations
* - calls either the single-threaded or the multi-threaded event loop
*
* Note: this is currently implemented as a macro.
*
* @param argc the argument counter passed to the main() function
* @param argv the argument vector passed to the main() function
* @param op the file system operation
* @return 0 on success, nonzero on failure
*/
/*
int fuse_main(int argc, char *argv[], const struct fuse_operations *op);
*/
#define fuse_main(argc, argv, op) \
fuse_main_real(argc, argv, op, sizeof(*(op)))
/* ----------------------------------------------------------- *
* More detailed API *
* ----------------------------------------------------------- */
/**
* Create a new FUSE filesystem.
*
* @param ch the communication channel
* @param args argument vector
* @param op the filesystem operations
* @param op_size the size of the fuse_operations structure
* @return the created FUSE handle
*/
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
const struct fuse_operations *op, size_t op_size);
/**
* Destroy the FUSE handle.
*
* The communication channel attached to the handle is also destroyed.
*
* NOTE: This function does not unmount the filesystem. If this is
* needed, call fuse_unmount() before calling this function.
*
* @param f the FUSE handle
*/
void fuse_destroy(struct fuse *f);
/**
* Exit from event loop
*
* @param f the FUSE handle
*/
void fuse_exit(struct fuse *f);
int fuse_config_read_thread_count(const struct fuse *f);
int fuse_config_process_thread_count(const struct fuse *f);
int fuse_config_process_thread_queue_depth(const struct fuse *f);
const char* fuse_config_pin_threads(const struct fuse *f);
/**
* FUSE event loop with multiple threads
*
* Requests from the kernel are processed, and the appropriate
* operations are called. Request are processed in parallel by
* distributing them between multiple threads.
*
* Calling this function requires the pthreads library to be linked to
* the application.
*
* @param f the FUSE handle
* @return 0 if no error occurred, -1 otherwise
*/
int fuse_loop_mt(struct fuse *f);
/**
* Get the current context
*
* The context is only valid for the duration of a filesystem
* operation, and thus must not be stored and used later.
*
* @return the context
*/
struct fuse_context *fuse_get_context(void);
/**
* Check if the current request has already been interrupted
*
* @return 1 if the request has been interrupted, 0 otherwise
*/
int fuse_interrupted(void);
/**
* Obsolete, doesn't do anything
*
* @return -EINVAL
*/
int fuse_invalidate(struct fuse *f, const char *path);
/* Deprecated, don't use */
int fuse_is_lib_option(const char *opt);
/**
* The real main function
*
* Do not call this directly, use fuse_main()
*/
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size);
void fuse_populate_maintenance_thread(struct fuse *fuse);
int fuse_log_metrics_get(void);
void fuse_log_metrics_set(int enabled);
/**
* Iterate over cache removing stale entries
* use in conjunction with "-oremember"
*
* NOTE: This is already done for the standard sessions
*
* @param fuse struct fuse pointer for fuse instance
* @return the number of seconds until the next cleanup
*/
int fuse_clean_cache(struct fuse *fuse);
/*
* Stacking API
*/
/**
* Fuse filesystem object
*
* This is opaque object represents a filesystem layer
*/
struct fuse_fs;
/*
* These functions call the relevant filesystem operation, and return
* the result.
*
* If the operation is not defined, they return -ENOSYS, with the
* exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir,
* fuse_fs_releasedir and fuse_fs_statfs, which return 0.
*/
int fuse_notify_poll(fuse_pollhandle_t *ph);
/**
* Create a new fuse filesystem object
*
* This is usually called from the factory of a fuse module to create
* a new instance of a filesystem.
*
* @param op the filesystem operations
* @param op_size the size of the fuse_operations structure
* @return a new filesystem object
*/
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size);
/* ----------------------------------------------------------- *
* Advanced API for event handling, don't worry about this... *
* ----------------------------------------------------------- */
/* NOTE: the following functions are deprecated, and will be removed
from the 3.0 API. Use the lowlevel session functions instead */
/** Function type used to process commands */
typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
/** This is the part of fuse_main() before the event loop */
struct fuse *fuse_setup(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size,
char **mountpoint);
/** This is the part of fuse_main() after the event loop */
void fuse_teardown(struct fuse *fuse, char *mountpoint);
/** 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);
/** Return the exited flag, which indicates if fuse_exit() has been
called */
int fuse_exited(struct fuse *f);
/** This function is obsolete and implemented as a no-op */
void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
/** Get session from fuse object */
struct fuse_session *fuse_get_session(struct fuse *f);
void fuse_gc1();
void fuse_gc();
void fuse_invalidate_all_nodes();
int fuse_get_dev_fuse_fd(const struct fuse_context *fc);
int fuse_passthrough_open(const struct fuse_context *fc,
const int fd);
int fuse_passthrough_close(const struct fuse_context *fc,
const int backing_id);
EXTERN_C_END
#endif /* _FUSE_H_ */
-80
View File
@@ -1,80 +0,0 @@
/*
ISC License
Copyright (c) 2019, 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 "extern_c.h"
EXTERN_C_BEGIN
#include "kvec.h"
#include "fuse_dirent.h"
#include "fuse_direntplus.h"
#include "fuse_entry.h"
#include "linux_dirent64.h"
#include <dirent.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
enum fuse_dirents_type_e
{
UNSET = 0,
NORMAL,
PLUS
};
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
typedef struct fuse_dirents_t fuse_dirents_t;
struct fuse_dirents_t
{
kvec_t(char) data;
kvec_t(uint32_t) offs;
fuse_dirents_type_t type;
};
int fuse_dirents_init(fuse_dirents_t *d);
void fuse_dirents_free(fuse_dirents_t *d);
void fuse_dirents_reset(fuse_dirents_t *d);
int fuse_dirents_add(fuse_dirents_t *d,
const struct dirent *de,
const uint64_t namelen);
int fuse_dirents_add_plus(fuse_dirents_t *d,
const struct dirent *de,
const uint64_t namelen,
const fuse_entry_t *entry,
const struct stat *st);
int fuse_dirents_add_linux(fuse_dirents_t *d,
const linux_dirent64_t *de,
const uint64_t namelen);
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
const linux_dirent64_t *de,
const uint64_t namelen,
const fuse_entry_t *entry,
const struct stat *st);
void *fuse_dirents_find(fuse_dirents_t *d,
const uint64_t ino);
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
EXTERN_C_END
-16
View File
@@ -1,16 +0,0 @@
#pragma once
#include <stdint.h>
#include <string.h>
#define DIRENT_NAMELEN(X) (strlen((X)->name))
typedef struct linux_dirent64_t linux_dirent64_t;
struct linux_dirent64_t
{
uint64_t ino;
int64_t off;
uint16_t reclen;
uint8_t type;
char name[];
};
-7
View File
@@ -1,7 +0,0 @@
#pragma once
#ifdef DEBUG
#include "mutex_debug.hpp"
#else
#include "mutex_ndebug.hpp"
#endif
-132
View File
@@ -1,132 +0,0 @@
#pragma once
#include "fmt/core.h"
#include <cstdlib>
#include <pthread.h>
#include <time.h>
#ifndef PTHREAD_MUTEX_ADAPTIVE_NP
# define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_NORMAL
#endif
#define mutex_init(M) _mutex_init(M,__FILE__,__func__,__LINE__)
#define mutex_destroy(M) _mutex_destroy(M,__FILE__,__func__,__LINE__)
#define mutex_lock(M) _mutex_lock(M,__FILE__,__func__,__LINE__)
#define mutex_unlock(M) _mutex_unlock(M,__FILE__,__func__,__LINE__)
static
inline
void
_print_error_and_abort(const char *func1_,
const int errno_,
const char *file_,
const char *func2_,
const int linenum_)
{
fmt::println(stderr,
"ERROR: {} returned {} ({}) - ({}:{}:{})",
func1_,
errno_,
strerror(errno_),
file_,
func2_,
linenum_);
abort();
}
static
inline
void
_mutex_init(pthread_mutex_t *mutex_,
const char *file_,
const char *func_,
const int linenum_)
{
int rv;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
rv = pthread_mutex_init(mutex_,&attr);
if(rv != 0)
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
pthread_mutexattr_destroy(&attr);
}
static
inline
void
_mutex_destroy(pthread_mutex_t *mutex_,
const char *file_,
const char *func_,
const int linenum_)
{
int rv;
rv = pthread_mutex_destroy(mutex_);
if(rv != 0)
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
}
static
inline
void
_mutex_lock(pthread_mutex_t *mutex_,
const char *file_,
const char *func_,
const int linenum_)
{
int rv;
int cnt = 0;
timespec timeout;
while(true)
{
clock_gettime(CLOCK_REALTIME,&timeout);
timeout.tv_nsec += 1000000 * 1; // 1ms
if(timeout.tv_nsec >= 1000000000)
{
timeout.tv_sec += 1;
timeout.tv_nsec -= 1000000000;
}
rv = pthread_mutex_timedlock(mutex_,&timeout);
switch(rv)
{
case 0:
return;
case ETIMEDOUT:
cnt++;
fmt::println(stderr,
"NOTICE: pthread_mutex_timedlock expired - count={}; ({}:{}:{})",
cnt,
file_,
func_,
linenum_);
continue;
default:
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
return;
}
}
}
static
inline
void
_mutex_unlock(pthread_mutex_t *mutex_,
const char *file_,
const char *func_,
const int linenum_)
{
int rv;
rv = pthread_mutex_unlock(mutex_);
if(rv != 0)
_print_error_and_abort(__func__,rv,file_,func_,linenum_);
}
-64
View File
@@ -1,64 +0,0 @@
#pragma once
#include <cstdlib>
#include <pthread.h>
#ifndef PTHREAD_MUTEX_ADAPTIVE_NP
# define PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_NORMAL
#endif
static
inline
void
mutex_init(pthread_mutex_t *mutex_)
{
int rv;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ADAPTIVE_NP);
rv = pthread_mutex_init(mutex_,&attr);
if(rv != 0)
abort();
pthread_mutexattr_destroy(&attr);
}
static
inline
void
mutex_lock(pthread_mutex_t *mutex_)
{
int rv;
rv = pthread_mutex_lock(mutex_);
if(rv != 0)
abort();
}
static
inline
void
mutex_unlock(pthread_mutex_t *mutex_)
{
int rv;
rv = pthread_mutex_unlock(mutex_);
if(rv != 0)
abort();
}
static
inline
void
mutex_destroy(pthread_mutex_t *mutex_)
{
int rv;
rv = pthread_mutex_destroy(mutex_);
if(rv != 0)
abort();
}
-389
View File
@@ -1,389 +0,0 @@
#pragma once
#include "moodycamel/blockingconcurrentqueue.h"
#include "moodycamel/lightweightsemaphore.h"
#include "invocable.h"
#include <algorithm>
#include <atomic>
#include <csignal>
#include <cstring>
#include <future>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
#include <utility>
#include <pthread.h>
#include <syslog.h>
#define SEM
//#undef SEM
#ifdef SEM
#define SEMA_WAIT(S) (S.wait())
#define SEMA_SIGNAL(S) (S.signal())
#else
#define SEMA_WAIT(S)
#define SEMA_SIGNAL(S)
#endif
struct ThreadPoolTraits : public moodycamel::ConcurrentQueueDefaultTraits
{
static const int MAX_SEMA_SPINS = 1;
};
class ThreadPool
{
public:
using PToken = moodycamel::ProducerToken;
using CToken = moodycamel::ConsumerToken;
private:
using Func = ofats::any_invocable<void()>;
using Queue = moodycamel::BlockingConcurrentQueue<Func,ThreadPoolTraits>;
public:
explicit
ThreadPool(const unsigned thread_count_ = std::thread::hardware_concurrency(),
const unsigned max_queue_depth_ = std::thread::hardware_concurrency(),
std::string const name_ = {});
~ThreadPool();
private:
static void *start_routine(void *arg_);
public:
int add_thread(std::string const name = {});
int remove_thread(void);
int set_threads(const std::size_t count);
void shutdown(void);
public:
template<typename FuncType>
void
enqueue_work(ThreadPool::PToken &ptok_,
FuncType &&func_);
template<typename FuncType>
void
enqueue_work(FuncType &&func_);
template<typename FuncType>
[[nodiscard]]
std::future<typename std::result_of<FuncType()>::type>
enqueue_task(FuncType&& func_);
public:
std::vector<pthread_t> threads() const;
ThreadPool::PToken ptoken();
private:
Queue _queue;
moodycamel::LightweightSemaphore _sema;
private:
std::string const _name;
std::vector<pthread_t> _threads;
mutable std::mutex _threads_mutex;
};
inline
ThreadPool::ThreadPool(const unsigned thread_count_,
const unsigned max_queue_depth_,
const std::string name_)
: _queue(),
_sema(max_queue_depth_),
_name(name_)
{
sigset_t oldset;
sigset_t newset;
sigfillset(&newset);
pthread_sigmask(SIG_BLOCK,&newset,&oldset);
_threads.reserve(thread_count_);
for(std::size_t i = 0; i < thread_count_; ++i)
{
int rv;
pthread_t t;
rv = pthread_create(&t,NULL,ThreadPool::start_routine,this);
if(rv != 0)
{
syslog(LOG_WARNING,
"threadpool (%s): error spawning thread - %d (%s)",
_name.c_str(),
rv,
strerror(rv));
continue;
}
if(!_name.empty())
pthread_setname_np(t,_name.c_str());
_threads.push_back(t);
}
pthread_sigmask(SIG_SETMASK,&oldset,NULL);
if(_threads.empty())
throw std::runtime_error("threadpool: failed to spawn any threads");
syslog(LOG_DEBUG,
"threadpool (%s): spawned %zu threads w/ max queue depth %u",
_name.c_str(),
_threads.size(),
max_queue_depth_);
}
inline
ThreadPool::~ThreadPool()
{
syslog(LOG_DEBUG,
"threadpool (%s): destroying %zu threads",
_name.c_str(),
_threads.size());
for(auto t : _threads)
pthread_cancel(t);
for(auto t : _threads)
pthread_join(t,NULL);
}
inline
void*
ThreadPool::start_routine(void *arg_)
{
ThreadPool *btp = static_cast<ThreadPool*>(arg_);
bool done;
ThreadPool::Func func;
ThreadPool::Queue &q = btp->_queue;
moodycamel::LightweightSemaphore &sema = btp->_sema;
ThreadPool::CToken ctok(btp->_queue);
done = false;
while(!done)
{
q.wait_dequeue(ctok,func);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
try
{
func();
}
catch(std::exception &e)
{
done = true;
}
SEMA_SIGNAL(sema);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
}
return nullptr;
}
inline
int
ThreadPool::add_thread(const std::string name_)
{
int rv;
pthread_t t;
sigset_t oldset;
sigset_t newset;
std::string name;
name = (name_.empty() ? _name : name_);
sigfillset(&newset);
pthread_sigmask(SIG_BLOCK,&newset,&oldset);
rv = pthread_create(&t,NULL,ThreadPool::start_routine,this);
pthread_sigmask(SIG_SETMASK,&oldset,NULL);
if(rv != 0)
{
syslog(LOG_WARNING,
"threadpool (%s): error spawning thread - %d (%s)",
_name.c_str(),
rv,
strerror(rv));
return -rv;
}
if(!name.empty())
pthread_setname_np(t,name.c_str());
{
std::lock_guard<std::mutex> lg(_threads_mutex);
_threads.push_back(t);
}
return 0;
}
inline
int
ThreadPool::remove_thread(void)
{
{
std::lock_guard<std::mutex> lg(_threads_mutex);
if(_threads.size() <= 1)
return -EINVAL;
}
std::promise<pthread_t> promise;
auto func =
[this,&promise]()
{
pthread_t t;
t = pthread_self();
promise.set_value(t);
{
std::lock_guard<std::mutex> lg(_threads_mutex);
for(auto i = _threads.begin(); i != _threads.end(); ++i)
{
if(*i != t)
continue;
_threads.erase(i);
break;
}
}
pthread_exit(NULL);
};
enqueue_work(std::move(func));
pthread_join(promise.get_future().get(),NULL);
return 0;
}
inline
int
ThreadPool::set_threads(std::size_t const count_)
{
int diff;
{
std::lock_guard<std::mutex> lg(_threads_mutex);
diff = ((int)count_ - (int)_threads.size());
}
for(auto i = diff; i > 0; --i)
add_thread();
for(auto i = diff; i < 0; ++i)
remove_thread();
return diff;
}
inline
void
ThreadPool::shutdown(void)
{
std::lock_guard<std::mutex> lg(_threads_mutex);
for(pthread_t tid : _threads)
pthread_cancel(tid);
for(pthread_t tid : _threads)
pthread_join(tid,NULL);
_threads.clear();
}
template<typename FuncType>
inline
void
ThreadPool::enqueue_work(ThreadPool::PToken &ptok_,
FuncType &&func_)
{
SEMA_WAIT(_sema);
_queue.enqueue(ptok_,
std::forward<FuncType>(func_));
}
template<typename FuncType>
inline
void
ThreadPool::enqueue_work(FuncType &&func_)
{
SEMA_WAIT(_sema);
_queue.enqueue(std::forward<FuncType>(func_));
}
template<typename FuncType>
[[nodiscard]]
inline
std::future<typename std::result_of<FuncType()>::type>
ThreadPool::enqueue_task(FuncType&& func_)
{
using TaskReturnType = typename std::result_of<FuncType()>::type;
using Promise = std::promise<TaskReturnType>;
Promise promise;
auto future = promise.get_future();
auto work =
[promise_ = std::move(promise),
func_ = std::forward<FuncType>(func_)]() mutable
{
try
{
auto rv = func_();
promise_.set_value(std::move(rv));
}
catch(...)
{
promise_.set_exception(std::current_exception());
}
};
SEMA_WAIT(_sema);
_queue.enqueue(std::move(work));
return future;
}
inline
std::vector<pthread_t>
ThreadPool::threads() const
{
std::lock_guard<std::mutex> lg(_threads_mutex);
return _threads;
}
inline
ThreadPool::PToken
ThreadPool::ptoken()
{
return ThreadPool::PToken(_queue);
}
-316
View File
@@ -1,316 +0,0 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB
*/
#define _GNU_SOURCE
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
{
size_t i;
size_t size = 0;
for (i = 0; i < bufv->count; i++) {
if (bufv->buf[i].size == SIZE_MAX)
size = SIZE_MAX;
else
size += bufv->buf[i].size;
}
return size;
}
static size_t min_size(size_t s1, size_t s2)
{
return s1 < s2 ? s1 : s2;
}
static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
ssize_t res = 0;
size_t copied = 0;
while (len) {
if (dst->flags & FUSE_BUF_FD_SEEK) {
res = pwrite(dst->fd, src->mem + src_off, len,
dst->pos + dst_off);
} else {
res = write(dst->fd, src->mem + src_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
copied += res;
if (!(dst->flags & FUSE_BUF_FD_RETRY))
break;
src_off += res;
dst_off += res;
len -= res;
}
return copied;
}
static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
ssize_t res = 0;
size_t copied = 0;
while (len) {
if (src->flags & FUSE_BUF_FD_SEEK) {
res = pread(src->fd, dst->mem + dst_off, len,
src->pos + src_off);
} else {
res = read(src->fd, dst->mem + dst_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY))
break;
dst_off += res;
src_off += res;
len -= res;
}
return copied;
}
static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
char buf[4096];
struct fuse_buf tmp = {
.size = sizeof(buf),
.flags = 0,
};
ssize_t res;
size_t copied = 0;
tmp.mem = buf;
while (len) {
size_t this_len = min_size(tmp.size, len);
size_t read_len;
res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
read_len = res;
res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
copied += res;
if (res < this_len)
break;
dst_off += res;
src_off += res;
len -= res;
}
return copied;
}
#ifdef HAVE_SPLICE
static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
int splice_flags = 0;
off_t *srcpos = NULL;
off_t *dstpos = NULL;
off_t srcpos_val;
off_t dstpos_val;
ssize_t res;
size_t copied = 0;
if (flags & FUSE_BUF_SPLICE_MOVE)
splice_flags |= SPLICE_F_MOVE;
if (flags & FUSE_BUF_SPLICE_NONBLOCK)
splice_flags |= SPLICE_F_NONBLOCK;
if (src->flags & FUSE_BUF_FD_SEEK) {
srcpos_val = src->pos + src_off;
srcpos = &srcpos_val;
}
if (dst->flags & FUSE_BUF_FD_SEEK) {
dstpos_val = dst->pos + dst_off;
dstpos = &dstpos_val;
}
while (len) {
res = splice(src->fd, srcpos, dst->fd, dstpos, len, splice_flags);
if (res == -1) {
if (copied)
break;
if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
return -errno;
/* Maybe splice is not supported for this combination */
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
len);
}
if (res == 0)
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY) &&
!(dst->flags & FUSE_BUF_FD_RETRY)) {
break;
}
len -= res;
}
return copied;
}
#else
static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
(void) flags;
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
}
#endif
static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
int src_is_fd = src->flags & FUSE_BUF_IS_FD;
int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
if (!src_is_fd && !dst_is_fd) {
char *dstmem = dst->mem + dst_off;
char *srcmem = src->mem + src_off;
if (dstmem != srcmem) {
if (dstmem + len <= srcmem || srcmem + len <= dstmem)
memcpy(dstmem, srcmem, len);
else
memmove(dstmem, srcmem, len);
}
return len;
} else if (!src_is_fd) {
return fuse_buf_write(dst, dst_off, src, src_off, len);
} else if (!dst_is_fd) {
return fuse_buf_read(dst, dst_off, src, src_off, len);
} else if (flags & FUSE_BUF_NO_SPLICE) {
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
} else {
return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
}
}
static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
{
if (bufv->idx < bufv->count)
return &bufv->buf[bufv->idx];
else
return NULL;
}
static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
{
const struct fuse_buf *buf = fuse_bufvec_current(bufv);
bufv->off += len;
assert(bufv->off <= buf->size);
if (bufv->off == buf->size) {
assert(bufv->idx < bufv->count);
bufv->idx++;
if (bufv->idx == bufv->count)
return 0;
bufv->off = 0;
}
return 1;
}
ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
enum fuse_buf_copy_flags flags)
{
size_t copied = 0;
if (dstv == srcv)
return fuse_buf_size(dstv);
for (;;) {
const struct fuse_buf *src = fuse_bufvec_current(srcv);
const struct fuse_buf *dst = fuse_bufvec_current(dstv);
size_t src_len;
size_t dst_len;
size_t len;
ssize_t res;
if (src == NULL || dst == NULL)
break;
src_len = src->size - srcv->off;
dst_len = dst->size - dstv->off;
len = min_size(src_len, dst_len);
res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
if (res < 0) {
if (!copied)
return res;
break;
}
copied += res;
if (!fuse_bufvec_advance(srcv, res) ||
!fuse_bufvec_advance(dstv, res))
break;
if (res < len)
break;
}
return copied;
}
File diff suppressed because it is too large Load Diff
-380
View File
@@ -1,380 +0,0 @@
/*
ISC License
Copyright (c) 2021, 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 "kvec.h"
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#define ROUND_UP(N,S) ((((N) + (S) - 1) / (S)) * (S))
typedef kvec_t(void*) slab_kvec_t;
typedef struct mem_stack_t mem_stack_t;
struct mem_stack_t
{
mem_stack_t *next;
};
typedef struct fmp_t fmp_t;
struct fmp_t
{
mem_stack_t *objs;
slab_kvec_t slabs;
uint64_t avail_objs;
uint64_t obj_size;
uint64_t page_size;
uint64_t slab_size;
};
static
inline
uint64_t
fmp_page_size()
{
return sysconf(_SC_PAGESIZE);
}
static
inline
void
fmp_init(fmp_t *fmp_,
const uint64_t obj_size_,
const uint64_t page_multiple_)
{
kv_init(fmp_->slabs);
fmp_->objs = NULL;
fmp_->avail_objs = 0;
fmp_->obj_size = ROUND_UP(obj_size_,sizeof(void*));
fmp_->page_size = fmp_page_size();
fmp_->slab_size = (fmp_->page_size * page_multiple_);
}
static
inline
uint64_t
fmp_slab_count(fmp_t *fmp_)
{
return kv_size(fmp_->slabs);
}
static
inline
void*
fmp_slab_alloc_posix_memalign(fmp_t *fmp_)
{
int rv;
void *mem;
const size_t alignment = fmp_->page_size;
const size_t size = fmp_->slab_size;
rv = posix_memalign(&mem,alignment,size);
if(rv != 0)
return NULL;
return NULL;
}
static
inline
void*
fmp_slab_alloc_mmap(fmp_t *fmp_)
{
void *mem;
void *address = NULL;
const size_t length = fmp_->slab_size;
const int protect = PROT_READ|PROT_WRITE;
const int flags = MAP_PRIVATE|MAP_ANONYMOUS;
const int filedes = -1;
const off_t offset = 0;
mem = mmap(address,length,protect,flags,filedes,offset);
if(mem == MAP_FAILED)
return NULL;
return mem;
}
static
inline
void
fmp_slab_free_posix_memalign(fmp_t* fmp_,
void *mem_)
{
(void)fmp_;
free(mem_);
}
static
inline
void
fmp_slab_free_mmap(fmp_t* fmp_,
void *mem_)
{
void *addr = mem_;
size_t length = fmp_->slab_size;
(void)munmap(addr,length);
}
static
inline
int
fmp_slab_alloc(fmp_t *fmp_)
{
char *i;
void *mem;
mem = fmp_slab_alloc_mmap(fmp_);
if(mem == NULL)
return -ENOMEM;
kv_push(void*,fmp_->slabs,mem);
i = ((char*)mem + fmp_->slab_size - fmp_->obj_size);
while(i >= (char*)mem)
{
mem_stack_t *obj = (mem_stack_t*)i;
obj->next = fmp_->objs;
fmp_->objs = obj;
fmp_->avail_objs++;
i -= fmp_->obj_size;
}
return 0;
}
static
inline
void*
fmp_alloc(fmp_t *fmp_)
{
void *rv;
if(fmp_->objs == NULL)
fmp_slab_alloc(fmp_);
if(fmp_->objs == NULL)
return NULL;
rv = fmp_->objs;
fmp_->objs = fmp_->objs->next;
fmp_->avail_objs--;
return rv;
}
static
inline
void*
fmp_calloc(fmp_t *fmp_)
{
void *obj;
obj = fmp_alloc(fmp_);
if(obj == NULL)
return NULL;
memset(obj,0,fmp_->obj_size);
return obj;
}
static
inline
void
fmp_free(fmp_t *fmp_,
void *obj_)
{
mem_stack_t *obj = (mem_stack_t*)obj_;
obj->next = fmp_->objs;
fmp_->objs = obj;
fmp_->avail_objs++;
}
static
inline
void
fmp_clear(fmp_t *fmp_)
{
while(kv_size(fmp_->slabs))
{
void *slab = kv_pop(fmp_->slabs);
fmp_slab_free_mmap(fmp_,slab);
}
fmp_->objs = NULL;
fmp_->avail_objs = 0;
}
static
inline
void
fmp_destroy(fmp_t *fmp_)
{
fmp_clear(fmp_);
kv_destroy(fmp_->slabs);
}
static
inline
uint64_t
fmp_avail_objs(fmp_t *fmp_)
{
return fmp_->avail_objs;
}
static
inline
uint64_t
fmp_objs_per_slab(fmp_t *fmp_)
{
return (fmp_->slab_size / fmp_->obj_size);
}
static
inline
uint64_t
fmp_objs_in_slab(fmp_t *fmp_,
void *slab_)
{
char *slab;
uint64_t objs_per_slab;
uint64_t objs_in_slab;
slab = (char*)slab_;
objs_in_slab = 0;
objs_per_slab = fmp_objs_per_slab(fmp_);
for(mem_stack_t *stack = fmp_->objs; stack != NULL; stack = stack->next)
{
char *obj = (char*)stack;
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
objs_in_slab++;
if(objs_in_slab >= objs_per_slab)
break;
}
return objs_in_slab;
}
static
inline
void
fmp_remove_objs_in_slab(fmp_t *fmp_,
void *slab_)
{
char *slab;
uint64_t objs_per_slab;
uint64_t objs_in_slab;
mem_stack_t **p;
p = &fmp_->objs;
slab = (char*)slab_;
objs_in_slab = 0;
objs_per_slab = fmp_objs_per_slab(fmp_);
while((*p) != NULL)
{
char *obj = (char*)*p;
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
{
objs_in_slab++;
*p = (*p)->next;
fmp_->avail_objs--;
if(objs_in_slab >= objs_per_slab)
break;
continue;
}
p = &(*p)->next;
}
}
static
inline
int
fmp_gc_slab(fmp_t *fmp_,
uint64_t slab_idx_)
{
char *slab;
uint64_t objs_in_slab;
uint64_t objs_per_slab;
slab_idx_ = (slab_idx_ % kv_size(fmp_->slabs));
slab = (char*)kv_A(fmp_->slabs,slab_idx_);
objs_per_slab = fmp_objs_per_slab(fmp_);
objs_in_slab = fmp_objs_in_slab(fmp_,slab);
if(objs_in_slab != objs_per_slab)
return 0;
fmp_remove_objs_in_slab(fmp_,slab);
kv_delete(fmp_->slabs,slab_idx_);
fmp_slab_free_mmap(fmp_,slab);
return 1;
}
static
inline
int
fmp_gc(fmp_t *fmp_)
{
uint64_t slab_idx;
slab_idx = rand();
return fmp_gc_slab(fmp_,slab_idx);
}
static
inline
double
fmp_slab_usage_ratio(fmp_t *fmp_)
{
double avail_objs;
double objs_per_slab;
double nums_of_slabs;
avail_objs = fmp_->avail_objs;
objs_per_slab = fmp_objs_per_slab(fmp_);
nums_of_slabs = kv_size(fmp_->slabs);
return (avail_objs / (objs_per_slab * nums_of_slabs));
}
static
inline
uint64_t
fmp_total_allocated_memory(fmp_t *fmp_)
{
return (fmp_->slab_size * kv_size(fmp_->slabs));
}
-4233
View File
File diff suppressed because it is too large Load Diff
-73
View File
@@ -1,73 +0,0 @@
/*
ISC License
Copyright (c) 2023, 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 <string>
static int g_READ_THREAD_COUNT = -1;
static int g_PROCESS_THREAD_COUNT = -1;
static int g_PROCESS_THREAD_QUEUE_DEPTH = -1;
static std::string g_PIN_THREADS = {};
int
fuse_config_get_read_thread_count()
{
return g_READ_THREAD_COUNT;
}
void
fuse_config_set_read_thread_count(int const v_)
{
g_READ_THREAD_COUNT = v_;
}
int
fuse_config_get_process_thread_count()
{
return g_PROCESS_THREAD_COUNT;
}
void
fuse_config_set_process_thread_count(int const v_)
{
g_PROCESS_THREAD_COUNT = v_;
}
int
fuse_config_get_process_thread_queue_depth()
{
return g_PROCESS_THREAD_QUEUE_DEPTH;
}
void
fuse_config_set_process_thread_queue_depth(int const v_)
{
g_PROCESS_THREAD_QUEUE_DEPTH = v_;
}
std::string
fuse_config_get_pin_threads()
{
return g_PIN_THREADS;
}
void
fuse_config_set_pin_threads(std::string const v_)
{
g_PIN_THREADS = v_;
}
-406
View File
@@ -1,406 +0,0 @@
#define _FILE_OFFSET_BITS 64
#include "fuse_attr.h"
#include "fuse_dirent.h"
#include "fuse_direntplus.h"
#include "fuse_dirents.h"
#include "fuse_entry.h"
#include "linux_dirent64.h"
#include "stat_utils.h"
#include <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 32KB - same as glibc getdents buffer size */
#define DEFAULT_SIZE (1024 * 32)
static
uint64_t
round_up(const uint64_t number_,
const uint64_t multiple_)
{
return (((number_ + multiple_ - 1) / multiple_) * multiple_);
}
static
uint64_t
align_uint64_t(uint64_t v_)
{
return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1));
}
static
uint64_t
fuse_dirent_size(const uint64_t namelen_)
{
uint64_t rv;
rv = offsetof(fuse_dirent_t,name);
rv += namelen_;
rv = align_uint64_t(rv);
return rv;
}
static
uint64_t
fuse_direntplus_size(const uint64_t namelen_)
{
uint64_t rv;
rv = offsetof(fuse_direntplus_t,dirent.name);
rv += namelen_;
rv = align_uint64_t(rv);
return rv;
}
static
int
fuse_dirents_buf_resize(fuse_dirents_t *d_,
uint64_t size_)
{
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
{
uint64_t new_size;
new_size = round_up((kv_size(d_->data) + size_),DEFAULT_SIZE);
kv_resize(char,d_->data,new_size);
if(d_->data.a == NULL)
return -ENOMEM;
}
return 0;
}
static
void*
fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
uint64_t namelen_)
{
int rv;
uint64_t size;
fuse_dirent_t *d;
size = fuse_dirent_size(namelen_);
rv = fuse_dirents_buf_resize(d_,size);
if(rv)
return NULL;
d = (fuse_dirent_t*)&kv_end(d_->data);
kv_size(d_->data) += size;
return d;
}
static
void*
fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
uint64_t namelen_)
{
int rv;
uint64_t size;
fuse_dirent_t *d;
size = fuse_direntplus_size(namelen_);
rv = fuse_dirents_buf_resize(d_,size);
if(rv)
return NULL;
d = (fuse_dirent_t*)&kv_end(d_->data);
kv_size(d_->data) += size;
return d;
}
static
void
fuse_dirents_fill_attr(fuse_attr_t *attr_,
const struct stat *st_)
{
attr_->ino = st_->st_ino;
attr_->size = st_->st_size;
attr_->blocks = st_->st_blocks;
attr_->atime = st_->st_atime;
attr_->mtime = st_->st_mtime;
attr_->ctime = st_->st_ctime;
attr_->atimensec = ST_ATIM_NSEC(st_);
attr_->mtimensec = ST_MTIM_NSEC(st_);
attr_->ctimensec = ST_CTIM_NSEC(st_);
attr_->mode = st_->st_mode;
attr_->nlink = st_->st_nlink;
attr_->uid = st_->st_uid;
attr_->gid = st_->st_gid;
attr_->rdev = st_->st_rdev;
attr_->blksize = st_->st_blksize;
}
fuse_dirent_t*
fuse_dirent_next(fuse_dirent_t *cur_)
{
char *buf;
buf = (char*)cur_;
buf += fuse_dirent_size(cur_->namelen);
return (fuse_dirent_t*)buf;
}
fuse_direntplus_t*
fuse_direntplus_next(fuse_direntplus_t *cur_)
{
char *buf;
buf = (char*)cur_;
buf += fuse_direntplus_size(cur_->dirent.namelen);
return (fuse_direntplus_t*)buf;
}
fuse_dirent_t*
fuse_dirent_find(fuse_dirents_t *d_,
const uint64_t ino_)
{
fuse_dirent_t *cur;
fuse_dirent_t *end;
if(d_->type != NORMAL)
return NULL;
cur = (fuse_dirent_t*)&kv_first(d_->data);
end = (fuse_dirent_t*)&kv_end(d_->data);
while(cur < end)
{
if(cur->ino == ino_)
return cur;
cur = fuse_dirent_next(cur);
}
return NULL;
}
fuse_direntplus_t*
fuse_direntplus_find(fuse_dirents_t *d_,
const uint64_t ino_)
{
fuse_direntplus_t *cur;
fuse_direntplus_t *end;
if(d_->type != PLUS)
return NULL;
cur = (fuse_direntplus_t*)&kv_first(d_->data);
end = (fuse_direntplus_t*)&kv_end(d_->data);
while(cur < end)
{
if(cur->dirent.ino == ino_)
return cur;
cur = fuse_direntplus_next(cur);
}
return NULL;
}
void*
fuse_dirents_find(fuse_dirents_t *d_,
const uint64_t ino_)
{
switch(d_->type)
{
default:
case UNSET:
return NULL;
case NORMAL:
return fuse_dirent_find(d_,ino_);
case PLUS:
return fuse_direntplus_find(d_,ino_);
}
}
int
fuse_dirents_convert_plus2normal(fuse_dirents_t *d_)
{
return -ENOSYS;
}
int
fuse_dirents_add(fuse_dirents_t *d_,
const struct dirent *dirent_,
const uint64_t namelen_)
{
fuse_dirent_t *d;
switch(d_->type)
{
case UNSET:
d_->type = NORMAL;
break;
case NORMAL:
break;
case PLUS:
return -EINVAL;
}
d = fuse_dirents_dirent_alloc(d_,namelen_);
if(d == NULL)
return -ENOMEM;
d->off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->ino = dirent_->d_ino;
d->namelen = namelen_;
d->type = dirent_->d_type;
memcpy(d->name,dirent_->d_name,namelen_);
return 0;
}
int
fuse_dirents_add_plus(fuse_dirents_t *d_,
const struct dirent *dirent_,
const uint64_t namelen_,
const fuse_entry_t *entry_,
const struct stat *st_)
{
fuse_direntplus_t *d;
switch(d_->type)
{
case UNSET:
d_->type = PLUS;
break;
case NORMAL:
return -EINVAL;
case PLUS:
break;
}
d = fuse_dirents_direntplus_alloc(d_,namelen_);
if(d == NULL)
return -ENOMEM;
d->dirent.off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->dirent.ino = dirent_->d_ino;
d->dirent.namelen = namelen_;
d->dirent.type = dirent_->d_type;
memcpy(d->dirent.name,dirent_->d_name,namelen_);
d->entry = *entry_;
fuse_dirents_fill_attr(&d->attr,st_);
return 0;
}
int
fuse_dirents_add_linux(fuse_dirents_t *d_,
const linux_dirent64_t *dirent_,
const uint64_t namelen_)
{
fuse_dirent_t *d;
switch(d_->type)
{
case UNSET:
d_->type = NORMAL;
break;
case NORMAL:
break;
case PLUS:
return -EINVAL;
}
d = fuse_dirents_dirent_alloc(d_,namelen_);
if(d == NULL)
return -ENOMEM;
d->off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->ino = dirent_->ino;
d->namelen = namelen_;
d->type = dirent_->type;
memcpy(d->name,dirent_->name,namelen_);
return 0;
}
int
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
const linux_dirent64_t *dirent_,
const uint64_t namelen_,
const fuse_entry_t *entry_,
const struct stat *st_)
{
fuse_direntplus_t *d;
switch(d_->type)
{
case UNSET:
d_->type = PLUS;
break;
case NORMAL:
return -EINVAL;
case PLUS:
break;
}
d = fuse_dirents_direntplus_alloc(d_,namelen_);
if(d == NULL)
return -ENOMEM;
d->dirent.off = kv_size(d_->offs);
kv_push(uint32_t,d_->offs,kv_size(d_->data));
d->dirent.ino = dirent_->ino;
d->dirent.namelen = namelen_;
d->dirent.type = dirent_->type;
memcpy(d->dirent.name,dirent_->name,namelen_);
d->entry = *entry_;
fuse_dirents_fill_attr(&d->attr,st_);
return 0;
}
void
fuse_dirents_reset(fuse_dirents_t *d_)
{
d_->type = UNSET;
kv_size(d_->data) = 0;
kv_size(d_->offs) = 1;
}
int
fuse_dirents_init(fuse_dirents_t *d_)
{
d_->type = UNSET;
kv_init(d_->data);
kv_resize(char,d_->data,DEFAULT_SIZE);
if(d_->data.a == NULL)
return -ENOMEM;
kv_init(d_->offs);
kv_resize(uint32_t,d_->offs,64);
kv_push(uint32_t,d_->offs,0);
return 0;
}
void
fuse_dirents_free(fuse_dirents_t *d_)
{
kv_destroy(d_->data);
kv_destroy(d_->offs);
}
-111
View File
@@ -1,111 +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
*/
#pragma once
#include "fuse.h"
#include "fuse_lowlevel.h"
#include "fuse_msgbuf.h"
#include "extern_c.h"
struct fuse_chan;
struct fuse_ll;
struct fuse_session
{
int (*receive_buf)(struct fuse_session *se,
fuse_msgbuf_t *msgbuf);
void (*process_buf)(struct fuse_session *se,
const fuse_msgbuf_t *msgbuf);
void (*destroy)(void *data);
struct fuse_ll *f;
volatile int exited;
struct fuse_chan *ch;
};
struct fuse_req
{
struct fuse_ll *f;
uint64_t unique;
struct fuse_ctx ctx;
struct fuse_chan *ch;
unsigned int ioctl_64bit : 1;
};
struct fuse_notify_req
{
uint64_t unique;
void (*reply)(struct fuse_notify_req *,
fuse_req_t,
uint64_t,
const void *);
struct fuse_notify_req *next;
struct fuse_notify_req *prev;
};
struct fuse_ll
{
int debug;
int no_remote_posix_lock;
int no_remote_flock;
int big_writes;
struct fuse_lowlevel_ops op;
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;
uint64_t notify_ctr;
struct fuse_notify_req notify_list;
};
struct fuse_cmd
{
char *buf;
size_t buflen;
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);
struct fuse_chan *fuse_kern_chan_new(int fd);
struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
int fuse_chan_clearfd(struct fuse_chan *ch);
void fuse_kern_unmount(const char *mountpoint, int fd);
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
int count);
void fuse_free_req(fuse_req_t req);
struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *fd);
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
EXTERN_C_END
-9
View File
@@ -1,9 +0,0 @@
#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);
-228
View File
@@ -1,228 +0,0 @@
/*
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.hpp"
#include "fuse.h"
#include "fuse_kernel.h"
#include <unistd.h>
#include <atomic>
#include <cstdint>
#include <cstdlib>
#include <mutex>
#include <vector>
static std::uint32_t g_PAGESIZE = 0;
static std::uint32_t g_BUFSIZE = 0;
static std::atomic<std::uint_fast64_t> g_MSGBUF_ALLOC_COUNT;
static std::mutex g_MUTEX;
static std::vector<fuse_msgbuf_t*> g_MSGBUF_STACK;
uint64_t
msgbuf_get_bufsize()
{
return g_BUFSIZE;
}
void
msgbuf_set_bufsize(const uint32_t size_in_pages_)
{
g_BUFSIZE = ((size_in_pages_ + 1) * g_PAGESIZE);
}
void
msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
{
msgbuf_->mem = (char*)msgbuf_;
msgbuf_->mem += g_PAGESIZE;
msgbuf_->size = (g_BUFSIZE - g_PAGESIZE);
}
void
msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
{
msgbuf_->mem = (char*)msgbuf_;
msgbuf_->mem += g_PAGESIZE;
msgbuf_->mem -= sizeof(struct fuse_in_header);
msgbuf_->mem -= sizeof(struct fuse_write_in);
msgbuf_->size = (g_BUFSIZE - g_PAGESIZE);
}
static
__attribute__((constructor))
void
msgbuf_constructor()
{
g_PAGESIZE = sysconf(_SC_PAGESIZE);
// FUSE_MAX_MAX_PAGES for payload + 1 for message header
msgbuf_set_bufsize(FUSE_DEFAULT_MAX_MAX_PAGES + 1);
}
static
__attribute__((destructor))
void
msgbuf_destroy()
{
}
static
void*
page_aligned_malloc(const uint64_t size_)
{
int rv;
void *buf = NULL;
rv = posix_memalign(&buf,g_PAGESIZE,size_);
if(rv != 0)
return NULL;
return buf;
}
typedef void (*msgbuf_setup_func_t)(fuse_msgbuf_t*);
static
fuse_msgbuf_t*
_msgbuf_alloc(msgbuf_setup_func_t setup_func_)
{
fuse_msgbuf_t *msgbuf;
g_MUTEX.lock();
if(g_MSGBUF_STACK.empty())
{
g_MUTEX.unlock();
msgbuf = (fuse_msgbuf_t*)page_aligned_malloc(g_BUFSIZE);
if(msgbuf == NULL)
return NULL;
g_MSGBUF_ALLOC_COUNT.fetch_add(1,std::memory_order_relaxed);
}
else
{
msgbuf = g_MSGBUF_STACK.back();
g_MSGBUF_STACK.pop_back();
g_MUTEX.unlock();
}
setup_func_(msgbuf);
return msgbuf;
}
// Offset the memory so write request payload will be placed on page
// boundry so O_DIRECT can work. No impact on other message types
// except for `read` which will require using `msgbuf_page_align`.
fuse_msgbuf_t*
msgbuf_alloc()
{
return _msgbuf_alloc(msgbuf_write_align);
}
fuse_msgbuf_t*
msgbuf_alloc_page_aligned()
{
return _msgbuf_alloc(msgbuf_page_align);
}
static
void
msgbuf_destroy(fuse_msgbuf_t *msgbuf_)
{
// free(msgbuf_->mem);
free(msgbuf_);
}
void
msgbuf_free(fuse_msgbuf_t *msgbuf_)
{
std::lock_guard<std::mutex> lck(g_MUTEX);
if(msgbuf_->size != (g_BUFSIZE - g_PAGESIZE))
{
msgbuf_destroy(msgbuf_);
g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
return;
}
g_MSGBUF_STACK.emplace_back(msgbuf_);
}
uint64_t
msgbuf_alloc_count()
{
return g_MSGBUF_ALLOC_COUNT;
}
uint64_t
msgbuf_avail_count()
{
std::lock_guard<std::mutex> lck(g_MUTEX);
return g_MSGBUF_STACK.size();
}
void
msgbuf_gc_10percent()
{
std::vector<fuse_msgbuf_t*> togc;
{
std::size_t size;
std::size_t ten_percent;
std::lock_guard<std::mutex> lck(g_MUTEX);
size = g_MSGBUF_STACK.size();
ten_percent = (size / 10);
for(std::size_t i = 0; i < ten_percent; i++)
{
togc.push_back(g_MSGBUF_STACK.back());
g_MSGBUF_STACK.pop_back();
}
}
for(auto msgbuf : togc)
{
msgbuf_destroy(msgbuf);
g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
}
}
void
msgbuf_gc()
{
std::vector<fuse_msgbuf_t*> oldstack;
{
std::lock_guard<std::mutex> lck(g_MUTEX);
oldstack.swap(g_MSGBUF_STACK);
}
for(auto msgbuf: oldstack)
{
msgbuf_destroy(msgbuf);
g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
}
}
-154
View File
@@ -1,154 +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 <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
struct fuse_chan
{
struct fuse_session *se;
int fd;
size_t bufsize;
};
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) {
fprintf(stderr, "fuse: failed to allocate session\n");
return NULL;
}
memset(se, 0, sizeof(*se));
se->f = data;
se->receive_buf = receive_buf;
se->process_buf = process_buf;
se->destroy = destroy;
return se;
}
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
{
assert(se->ch == NULL);
assert(ch->se == NULL);
se->ch = ch;
ch->se = se;
}
void fuse_session_remove_chan(struct fuse_chan *ch)
{
struct fuse_session *se = ch->se;
if (se) {
assert(se->ch == ch);
se->ch = NULL;
ch->se = NULL;
}
}
void
fuse_session_destroy(struct fuse_session *se)
{
se->destroy(se->f);
if(se->ch != NULL)
fuse_chan_destroy(se->ch);
free(se);
}
void fuse_session_reset(struct fuse_session *se)
{
se->exited = 0;
}
int
fuse_session_exited(struct fuse_session *se)
{
return se->exited;
}
void
fuse_session_exit(struct fuse_session *se_)
{
se_->exited = 1;
}
void*
fuse_session_data(struct fuse_session *se)
{
return se->f;
}
struct fuse_chan *
fuse_chan_new(int fd,
size_t bufsize)
{
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->fd = fd;
ch->bufsize = bufsize;
return ch;
}
int fuse_chan_fd(struct fuse_chan *ch)
{
return ch->fd;
}
int fuse_chan_clearfd(struct fuse_chan *ch)
{
int fd = ch->fd;
ch->fd = -1;
return fd;
}
size_t fuse_chan_bufsize(struct fuse_chan *ch)
{
return ch->bufsize;
}
struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
{
return ch->se;
}
void
fuse_chan_destroy(struct fuse_chan *ch)
{
int fd;
fuse_session_remove_chan(ch);
fd = fuse_chan_fd(ch);
if(fd != -1)
close(fd);
free(ch);
}
-399
View File
@@ -1,399 +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_opt.h"
#include "fuse_lowlevel.h"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <sys/param.h>
enum {
KEY_HELP,
KEY_HELP_NOHEADER,
KEY_VERSION,
};
struct helper_opts
{
int foreground;
int nodefault_subtype;
char *mountpoint;
};
#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
static
const
struct fuse_opt fuse_helper_opts[] =
{
FUSE_HELPER_OPT("-f", foreground),
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_END
};
static void usage(const char *progname)
{
fprintf(stderr,
"usage: %s mountpoint [options]\n\n", progname);
fprintf(stderr,
"general options:\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n"
" -V --version print version\n"
"\n");
}
static void helper_help(void)
{
fprintf(stderr,
"FUSE options:\n"
" -d -o debug enable debug output (implies -f)\n"
" -f foreground operation\n"
"\n"
);
}
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct helper_opts *hopts = data;
switch (key) {
case KEY_HELP:
usage(outargs->argv[0]);
/* fall through */
case KEY_HELP_NOHEADER:
helper_help();
return fuse_opt_add_arg(outargs, "-h");
case KEY_VERSION:
return 1;
case FUSE_OPT_KEY_NONOPT:
if (!hopts->mountpoint) {
char mountpoint[PATH_MAX];
if (realpath(arg, mountpoint) == NULL) {
fprintf(stderr,
"fuse: bad mount point `%s': %s\n",
arg, strerror(errno));
return -1;
}
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
} else {
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
return -1;
}
default:
return 1;
}
}
static int add_default_subtype(const char *progname, struct fuse_args *args)
{
int res;
char *subtype_opt;
const char *basename = strrchr(progname, '/');
if (basename == NULL)
basename = progname;
else if (basename[1] != '\0')
basename++;
subtype_opt = (char *) malloc(strlen(basename) + 64);
if (subtype_opt == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
sprintf(subtype_opt, "-osubtype=%s", basename);
res = fuse_opt_add_arg(args, subtype_opt);
free(subtype_opt);
return res;
}
int
fuse_parse_cmdline(struct fuse_args *args_,
char **mountpoint_,
int *foreground_)
{
int res;
struct helper_opts hopts;
memset(&hopts, 0, sizeof(hopts));
res = fuse_opt_parse(args_,
&hopts,
fuse_helper_opts,
fuse_helper_opt_proc);
if(res == -1)
return -1;
if(!hopts.nodefault_subtype)
{
res = add_default_subtype(args_->argv[0], args_);
if(res == -1)
goto err;
}
if(mountpoint_)
*mountpoint_ = hopts.mountpoint;
else
free(hopts.mountpoint);
if(foreground_)
*foreground_ = hopts.foreground;
return 0;
err:
free(hopts.mountpoint);
return -1;
}
int fuse_daemonize(int foreground)
{
if (!foreground) {
int nullfd;
int waiter[2];
char completed;
if (pipe(waiter)) {
perror("fuse_daemonize: pipe");
return -1;
}
/*
* demonize current process by forking it and killing the
* parent. This makes current process as a child of 'init'.
*/
switch(fork()) {
case -1:
perror("fuse_daemonize: fork");
return -1;
case 0:
break;
default:
read(waiter[0], &completed, sizeof(completed));
_exit(0);
}
if (setsid() == -1) {
perror("fuse_daemonize: setsid");
return -1;
}
(void) chdir("/");
nullfd = open("/dev/null", O_RDWR, 0);
if (nullfd != -1) {
(void) dup2(nullfd, 0);
(void) dup2(nullfd, 1);
(void) dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
/* Propagate completion of daemon initializatation */
completed = 1;
write(waiter[1], &completed, sizeof(completed));
close(waiter[0]);
close(waiter[1]);
}
return 0;
}
static
struct fuse_chan *
fuse_mount_common(const char *mountpoint_,
struct fuse_args *args_)
{
int fd;
long bufsize;
long pagesize;
struct fuse_chan *ch;
/*
* Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
* would ensue.
*/
do
{
fd = open("/dev/null", O_RDWR);
if(fd > 2)
close(fd);
} while(fd >= 0 && fd <= 2);
fd = fuse_kern_mount(mountpoint_,args_);
if(fd == -1)
return NULL;
pagesize = sysconf(_SC_PAGESIZE);
bufsize = ((FUSE_DEFAULT_MAX_MAX_PAGES + 1) * pagesize);
ch = fuse_chan_new(fd,bufsize);
if(!ch)
fuse_kern_unmount(mountpoint_, fd);
return ch;
}
struct fuse_chan *
fuse_mount(const char *mountpoint_,
struct fuse_args *args_)
{
return fuse_mount_common(mountpoint_,args_);
}
static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
{
if (mountpoint) {
int fd = ch ? fuse_chan_clearfd(ch) : -1;
fuse_kern_unmount(mountpoint, fd);
if (ch)
fuse_chan_destroy(ch);
}
}
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_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_chan *ch;
struct fuse *fuse;
int foreground;
int res;
res = fuse_parse_cmdline(&args, mountpoint, &foreground);
if (res == -1)
return NULL;
ch = fuse_mount_common(*mountpoint, &args);
if (!ch) {
fuse_opt_free_args(&args);
goto err_free;
}
fuse = fuse_new_common(ch, &args, op, op_size);
fuse_opt_free_args(&args);
if (fuse == NULL)
goto err_unmount;
res = fuse_daemonize(foreground);
if (res == -1)
goto err_unmount;
res = fuse_set_signal_handlers(fuse_get_session(fuse));
if (res == -1)
goto err_unmount;
if (fd)
*fd = fuse_chan_fd(ch);
return fuse;
err_unmount:
fuse_unmount_common(*mountpoint, ch);
if (fuse)
fuse_destroy(fuse);
err_free:
free(*mountpoint);
return NULL;
}
struct fuse *fuse_setup(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size,
char **mountpoint)
{
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
NULL);
}
static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
{
struct fuse_session *se = fuse_get_session(fuse);
struct fuse_chan *ch = se->ch;
fuse_remove_signal_handlers(se);
fuse_unmount_common(mountpoint, ch);
fuse_destroy(fuse);
free(mountpoint);
}
void fuse_teardown(struct fuse *fuse, char *mountpoint)
{
fuse_teardown_common(fuse, mountpoint);
}
static int fuse_main_common(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size)
{
struct fuse *fuse;
char *mountpoint;
int res;
fuse = fuse_setup_common(argc, argv, op, op_size,
&mountpoint,
NULL);
if (fuse == NULL)
return 1;
res = fuse_loop_mt(fuse);
fuse_teardown_common(fuse, mountpoint);
if (res == -1)
return 1;
return 0;
}
int fuse_main_real(int argc,
char *argv[],
const struct fuse_operations *op,
size_t op_size)
{
return fuse_main_common(argc, argv, op, op_size);
}
#undef fuse_main
int fuse_main(void);
int fuse_main(void)
{
fprintf(stderr, "fuse_main(): This function does not exist\n");
return -1;
}
int fuse_version(void)
{
return FUSE_VERSION;
}
-627
View File
@@ -1,627 +0,0 @@
/* The MIT License
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
An example:
#include "khash.h"
KHASH_MAP_INIT_INT(32, char)
int main() {
int ret, is_missing;
khiter_t k;
khash_t(32) *h = kh_init(32);
k = kh_put(32, h, 5, &ret);
kh_value(h, k) = 10;
k = kh_get(32, h, 10);
is_missing = (k == kh_end(h));
k = kh_get(32, h, 5);
kh_del(32, h, k);
for (k = kh_begin(h); k != kh_end(h); ++k)
if (kh_exist(h, k)) kh_value(h, k) = 1;
kh_destroy(32, h);
return 0;
}
*/
/*
2013-05-02 (0.2.8):
* Use quadratic probing. When the capacity is power of 2, stepping function
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
hashing on cache performance and is more robust than linear probing.
In theory, double hashing should be more robust than quadratic probing.
However, my implementation is probably not for large hash tables, because
the second hash function is closely tied to the first hash function,
which reduce the effectiveness of double hashing.
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
2011-12-29 (0.2.7):
* Minor code clean up; no actual effect.
2011-09-16 (0.2.6):
* The capacity is a power of 2. This seems to dramatically improve the
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
- http://code.google.com/p/ulib/
- http://nothings.org/computer/judy/
* Allow to optionally use linear probing which usually has better
performance for random input. Double hashing is still the default as it
is more robust to certain non-random input.
* Added Wang's integer hash function (not used by default). This hash
function is more robust to certain non-random input.
2011-02-14 (0.2.5):
* Allow to declare global functions.
2009-09-26 (0.2.4):
* Improve portability
2008-09-19 (0.2.3):
* Corrected the example
* Improved interfaces
2008-09-11 (0.2.2):
* Improved speed a little in kh_put()
2008-09-10 (0.2.1):
* Added kh_clear()
* Fixed a compiling error
2008-09-02 (0.2.0):
* Changed to token concatenation which increases flexibility.
2008-08-31 (0.1.2):
* Fixed a bug in kh_get(), which has not been tested previously.
2008-08-31 (0.1.1):
* Added destructor
*/
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
/*!
@header
Generic hash table library.
*/
#define AC_VERSION_KHASH_H "0.2.8"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* compiler specific configuration */
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
#elif ULONG_MAX == 0xffffffffu
typedef unsigned long khint32_t;
#endif
#if ULONG_MAX == ULLONG_MAX
typedef unsigned long khint64_t;
#else
typedef unsigned long long khint64_t;
#endif
#ifndef kh_inline
#ifdef _MSC_VER
#define kh_inline __inline
#else
#define kh_inline inline
#endif
#endif /* kh_inline */
#ifndef klib_unused
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
#define klib_unused __attribute__ ((__unused__))
#else
#define klib_unused
#endif
#endif /* klib_unused */
typedef khint32_t khint_t;
typedef khint_t khiter_t;
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
#ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
#ifndef kcalloc
#define kcalloc(N,Z) calloc(N,Z)
#endif
#ifndef kmalloc
#define kmalloc(Z) malloc(Z)
#endif
#ifndef krealloc
#define krealloc(P,Z) realloc(P,Z)
#endif
#ifndef kfree
#define kfree(P) free(P)
#endif
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct kh_##name##_s { \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
khval_t *vals; \
} kh_##name##_t;
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(void); \
extern void kh_destroy_##name(kh_##name##_t *h); \
extern void kh_clear_##name(kh_##name##_t *h); \
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
SCOPE kh_##name##_t *kh_init_##name(void) { \
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
} \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
kfree((void *)h->keys); kfree(h->flags); \
kfree((void *)h->vals); \
kfree(h); \
} \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
i = (i + (++step)) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (!new_flags) return -1; \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (!new_keys) { kfree(new_flags); return -1; } \
h->keys = new_keys; \
if (kh_is_map) { \
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
if (!new_vals) { kfree(new_flags); return -1; } \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) h->vals[i] = val; \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
return 0; \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size<<1)) { \
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
*ret = -1; return h->n_buckets; \
} \
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
*ret = -1; return h->n_buckets; \
} \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
{ \
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
i = (i + (++step)) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
else x = i; \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; ++h->n_occupied; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; \
*ret = 2; \
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#define KHASH_DECLARE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
/* --- BEGIN OF HASH FUNCTIONS --- */
/*! @function
@abstract Integer hash function
@param key The integer [khint32_t]
@return The hash value [khint_t]
*/
#define kh_int_hash_func(key) (khint32_t)(key)
/*! @function
@abstract Integer comparison function
*/
#define kh_int_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract 64-bit integer hash function
@param key The integer [khint64_t]
@return The hash value [khint_t]
*/
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
/*! @function
@abstract 64-bit integer comparison function
*/
#define kh_int64_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract const char* hash function
@param s Pointer to a null terminated string
@return The hash value
*/
static kh_inline khint_t __ac_X31_hash_string(const char *s)
{
khint_t h = (khint_t)*s;
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
return h;
}
/*! @function
@abstract Another interface to const char* hash function
@param key Pointer to a null terminated string [const char*]
@return The hash value [khint_t]
*/
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
/*! @function
@abstract Const char* comparison function
*/
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
static kh_inline khint_t __ac_Wang_hash(khint_t key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
/* --- END OF HASH FUNCTIONS --- */
/* Other convenient macros... */
/*!
@abstract Type of the hash table.
@param name Name of the hash table [symbol]
*/
#define khash_t(name) kh_##name##_t
/*! @function
@abstract Initiate a hash table.
@param name Name of the hash table [symbol]
@return Pointer to the hash table [khash_t(name)*]
*/
#define kh_init(name) kh_init_##name()
/*! @function
@abstract Destroy a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_clear(name, h) kh_clear_##name(h)
/*! @function
@abstract Resize a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param s New size [khint_t]
*/
#define kh_resize(name, h, s) kh_resize_##name(h, s)
/*! @function
@abstract Insert a key to the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@param r Extra return code: -1 if the operation failed;
0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
*/
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
/*! @function
@abstract Retrieve a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
/*! @function
@abstract Remove a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Iterator to the element to be deleted [khint_t]
*/
#define kh_del(name, h, k) kh_del_##name(h, k)
/*! @function
@abstract Test whether a bucket contains data.
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return 1 if containing data; 0 otherwise [int]
*/
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
/*! @function
@abstract Get key given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Key [type of keys]
*/
#define kh_key(h, x) ((h)->keys[x])
/*! @function
@abstract Get value given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Value [type of values]
@discussion For hash sets, calling this results in segfault.
*/
#define kh_val(h, x) ((h)->vals[x])
/*! @function
@abstract Alias of kh_val()
*/
#define kh_value(h, x) ((h)->vals[x])
/*! @function
@abstract Get the start iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The start iterator [khint_t]
*/
#define kh_begin(h) (khint_t)(0)
/*! @function
@abstract Get the end iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The end iterator [khint_t]
*/
#define kh_end(h) ((h)->n_buckets)
/*! @function
@abstract Get the number of elements in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of elements in the hash table [khint_t]
*/
#define kh_size(h) ((h)->size)
/*! @function
@abstract Get the number of buckets in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of buckets in the hash table [khint_t]
*/
#define kh_n_buckets(h) ((h)->n_buckets)
/*! @function
@abstract Iterate over the entries in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
(vvar) = kh_val(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the values in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(vvar) = kh_val(h,__i); \
code; \
} }
/* More convenient interfaces */
/*! @function
@abstract Instantiate a hash set containing integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT(name) \
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT(name, khval_t) \
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash set containing 64-bit integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT64(name) \
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT64(name, khval_t) \
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
typedef const char *kh_cstr_t;
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_STR(name) \
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#endif /* __AC_KHASH_H */
-245
View File
@@ -1,245 +0,0 @@
/*
ISC License
Copyright (c) 2020, 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 "fmp.h"
#include "mutex.hpp"
#include <pthread.h>
typedef struct lfmp_t lfmp_t;
struct lfmp_t
{
fmp_t fmp;
pthread_mutex_t lock;
};
static
inline
void
lfmp_init(lfmp_t *lfmp_,
const uint64_t obj_size_,
const uint64_t page_multiple_)
{
fmp_init(&lfmp_->fmp,obj_size_,page_multiple_);
mutex_init(&lfmp_->lock);
}
static
inline
void
lfmp_lock(lfmp_t *lfmp_)
{
mutex_lock(&lfmp_->lock);
}
static
inline
void
lfmp_unlock(lfmp_t *lfmp_)
{
mutex_unlock(&lfmp_->lock);
}
static
inline
uint64_t
lfmp_slab_count(lfmp_t *lfmp_)
{
uint64_t rv;
mutex_lock(&lfmp_->lock);
rv = fmp_slab_count(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
int
lfmp_slab_alloc(lfmp_t *lfmp_)
{
int rv;
mutex_lock(&lfmp_->lock);
rv = fmp_slab_alloc(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void*
lfmp_alloc(lfmp_t *lfmp_)
{
void *rv;
mutex_lock(&lfmp_->lock);
rv = fmp_alloc(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void*
lfmp_calloc(lfmp_t *lfmp_)
{
void *rv;
mutex_lock(&lfmp_->lock);
rv = fmp_calloc(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void
lfmp_free(lfmp_t *lfmp_,
void *obj_)
{
mutex_lock(&lfmp_->lock);
fmp_free(&lfmp_->fmp,obj_);
mutex_unlock(&lfmp_->lock);
}
static
inline
void
lfmp_clear(lfmp_t *lfmp_)
{
mutex_lock(&lfmp_->lock);
fmp_clear(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
}
static
inline
void
lfmp_destroy(lfmp_t *lfmp_)
{
mutex_lock(&lfmp_->lock);
fmp_destroy(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
mutex_destroy(&lfmp_->lock);
}
static
inline
uint64_t
lfmp_avail_objs(lfmp_t *lfmp_)
{
uint64_t rv;
mutex_lock(&lfmp_->lock);
rv = fmp_avail_objs(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
uint64_t
lfmp_objs_in_slab(lfmp_t *lfmp_,
void *slab_)
{
uint64_t rv;
mutex_lock(&lfmp_->lock);
rv = fmp_objs_in_slab(&lfmp_->fmp,slab_);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
void
lfmp_remove_objs_in_slab(lfmp_t *lfmp_,
void *slab_)
{
mutex_lock(&lfmp_->lock);
fmp_remove_objs_in_slab(&lfmp_->fmp,slab_);
mutex_unlock(&lfmp_->lock);
}
static
inline
int
lfmp_gc(lfmp_t *lfmp_)
{
int rv;
mutex_lock(&lfmp_->lock);
rv = fmp_gc(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
uint64_t
lfmp_objs_per_slab(lfmp_t *lfmp_)
{
uint64_t rv;
mutex_lock(&lfmp_->lock);
rv = fmp_objs_per_slab(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
double
lfmp_slab_usage_ratio(lfmp_t *lfmp_)
{
double rv;
mutex_lock(&lfmp_->lock);
rv = fmp_slab_usage_ratio(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
static
inline
uint64_t
lfmp_total_allocated_memory(lfmp_t *lfmp_)
{
uint64_t rv;
mutex_lock(&lfmp_->lock);
rv = fmp_total_allocated_memory(&lfmp_->fmp);
mutex_unlock(&lfmp_->lock);
return rv;
}
-41
View File
@@ -1,41 +0,0 @@
#include <cstddef>
#include <memory>
#include <type_traits>
#include <utility>
namespace std
{
template<class T> struct _Unique_if
{
typedef unique_ptr<T> _Single_object;
};
template<class T> struct _Unique_if<T[]>
{
typedef unique_ptr<T[]> _Unknown_bound;
};
template<class T, size_t N> struct _Unique_if<T[N]>
{
typedef void _Known_bound;
};
template<class T, class... Args>
typename _Unique_if<T>::_Single_object
make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T>
typename _Unique_if<T>::_Unknown_bound
make_unique(size_t n)
{
typedef typename remove_extent<T>::type U;
return unique_ptr<T>(new U[n]());
}
template<class T, class... Args>
typename _Unique_if<T>::_Known_bound
make_unique(Args&&...) = delete;
}
-595
View File
@@ -1,595 +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_opt.h"
#include "mount_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/mount.h>
#ifdef __NetBSD__
#include <perfuse.h>
#define MS_RDONLY MNT_RDONLY
#define MS_NOSUID MNT_NOSUID
#define MS_NODEV MNT_NODEV
#define MS_NOEXEC MNT_NOEXEC
#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
#define MS_NOATIME MNT_NOATIME
#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
#endif
#define FUSERMOUNT_PROG "fusermount"
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
#ifndef MS_DIRSYNC
#define MS_DIRSYNC 128
#endif
enum {
KEY_KERN_FLAG,
KEY_KERN_OPT,
KEY_FUSERMOUNT_OPT,
KEY_SUBTYPE_OPT,
KEY_MTAB_OPT,
KEY_RO,
KEY_HELP,
KEY_VERSION,
};
struct mount_opts {
int allow_other;
int ishelp;
int flags;
int auto_unmount;
int blkdev;
char *fsname;
char *subtype;
char *subtype_opt;
char *mtab_opts;
char *fusermount_opts;
char *kernel_opts;
};
#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
static const struct fuse_opt fuse_mount_opts[] = {
FUSE_MOUNT_OPT("allow_other", allow_other),
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("auto_unmount", KEY_FUSERMOUNT_OPT),
FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
FUSE_OPT_KEY("context=", KEY_KERN_OPT),
FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
FUSE_OPT_KEY("async", KEY_KERN_FLAG),
FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
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 void mount_help(void)
{
fprintf(stderr,
" -o auto_unmount auto unmount on process termination\n"
" -o default_permissions enable permission checking by kernel\n"
" -o fsname=NAME set filesystem name\n"
" -o subtype=NAME set filesystem type\n"
" -o large_read issue large read requests (2.4 only)\n"
" -o max_read=N set maximum size of read requests\n"
"\n");
}
static void exec_fusermount(const char *argv[])
{
execv(FUSERMOUNT_DIR "/mergerfs-" FUSERMOUNT_PROG, (char **) argv);
execvp("mergerfs-" FUSERMOUNT_PROG, (char **) argv);
execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
execvp(FUSERMOUNT_PROG, (char **) argv);
}
static void mount_version(void)
{
int pid = fork();
if (!pid) {
const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
exec_fusermount(argv);
_exit(1);
} else if (pid != -1)
waitpid(pid, NULL, 0);
}
struct mount_flags {
const char *opt;
unsigned long flag;
int on;
};
static const struct mount_flags mount_flags[] = {
{"rw", MS_RDONLY, 0},
{"ro", MS_RDONLY, 1},
{"suid", MS_NOSUID, 0},
{"nosuid", MS_NOSUID, 1},
{"dev", MS_NODEV, 0},
{"nodev", MS_NODEV, 1},
{"exec", MS_NOEXEC, 0},
{"noexec", MS_NOEXEC, 1},
{"async", MS_SYNCHRONOUS, 0},
{"sync", MS_SYNCHRONOUS, 1},
{"atime", MS_NOATIME, 0},
{"noatime", MS_NOATIME, 1},
#ifndef __NetBSD__
{"dirsync", MS_DIRSYNC, 1},
#endif
{NULL, 0, 0}
};
static void set_mount_flag(const char *s, int *flags)
{
int i;
for (i = 0; mount_flags[i].opt != NULL; i++) {
const char *opt = mount_flags[i].opt;
if (strcmp(opt, s) == 0) {
if (mount_flags[i].on)
*flags |= mount_flags[i].flag;
else
*flags &= ~mount_flags[i].flag;
return;
}
}
fprintf(stderr, "fuse: internal error, can't find mount flag\n");
abort();
}
static int fuse_mount_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct mount_opts *mo = data;
switch (key) {
case KEY_RO:
arg = "ro";
/* fall through */
case KEY_KERN_FLAG:
set_mount_flag(arg, &mo->flags);
return 0;
case KEY_KERN_OPT:
return fuse_opt_add_opt(&mo->kernel_opts, arg);
case KEY_FUSERMOUNT_OPT:
return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
case KEY_SUBTYPE_OPT:
return fuse_opt_add_opt(&mo->subtype_opt, arg);
case KEY_MTAB_OPT:
return fuse_opt_add_opt(&mo->mtab_opts, arg);
case KEY_HELP:
mount_help();
mo->ishelp = 1;
break;
case KEY_VERSION:
mount_version();
mo->ishelp = 1;
break;
}
return 1;
}
/* return value:
* >= 0 => fd
* -1 => error
*/
static int receive_fd(int fd)
{
struct msghdr msg;
struct iovec iov;
char buf[1];
int rv;
size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
struct cmsghdr *cmsg;
iov.iov_base = buf;
iov.iov_len = 1;
memset(&msg, 0, sizeof(msg));
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* old BSD implementations should use msg_accrights instead of
* msg_control; the interface is different. */
msg.msg_control = ccmsg;
msg.msg_controllen = sizeof(ccmsg);
while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
if (rv == -1) {
perror("recvmsg");
return -1;
}
if(!rv) {
/* EOF */
return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg->cmsg_type != SCM_RIGHTS) {
fprintf(stderr, "got control message of unknown type %d\n",
cmsg->cmsg_type);
return -1;
}
return *(int*)CMSG_DATA(cmsg);
}
void fuse_kern_unmount(const char *mountpoint, int fd)
{
int res;
int pid;
if (!mountpoint)
return;
if (fd != -1) {
struct pollfd pfd;
pfd.fd = fd;
pfd.events = 0;
res = poll(&pfd, 1, 0);
/* Need to close file descriptor, otherwise synchronous umount
would recurse into filesystem, and deadlock.
Caller expects fuse_kern_unmount to close the fd, so close it
anyway. */
close(fd);
/* If file poll returns POLLERR on the device file descriptor,
then the filesystem is already unmounted */
if (res == 1 && (pfd.revents & POLLERR))
return;
}
if (geteuid() == 0) {
fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
return;
}
res = umount2(mountpoint, 2);
if (res == 0)
return;
pid = fork();
if(pid == -1)
return;
if(pid == 0) {
const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
"--", mountpoint, NULL };
exec_fusermount(argv);
_exit(1);
}
waitpid(pid, NULL, 0);
}
static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
const char *opts, int quiet)
{
int fds[2], pid;
int res;
int rv;
if (!mountpoint) {
fprintf(stderr, "fuse: missing mountpoint parameter\n");
return -1;
}
res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
if(res == -1) {
perror("fuse: socketpair() failed");
return -1;
}
pid = fork();
if(pid == -1) {
perror("fuse: fork() failed");
close(fds[0]);
close(fds[1]);
return -1;
}
if(pid == 0) {
char env[10];
const char *argv[32];
int a = 0;
if (quiet) {
int fd = open("/dev/null", O_RDONLY);
if (fd != -1) {
dup2(fd, 1);
dup2(fd, 2);
}
}
argv[a++] = FUSERMOUNT_PROG;
if (opts) {
argv[a++] = "-o";
argv[a++] = opts;
}
argv[a++] = "--";
argv[a++] = mountpoint;
argv[a++] = NULL;
close(fds[1]);
fcntl(fds[0], F_SETFD, 0);
snprintf(env, sizeof(env), "%i", fds[0]);
setenv(FUSE_COMMFD_ENV, env, 1);
exec_fusermount(argv);
perror("fuse: failed to exec fusermount");
_exit(1);
}
close(fds[0]);
rv = receive_fd(fds[1]);
if (!mo->auto_unmount) {
/* with auto_unmount option fusermount will not exit until
this socket is closed */
close(fds[1]);
waitpid(pid, NULL, 0); /* bury zombie */
}
return rv;
}
static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
const char *mnt_opts)
{
char tmp[128];
const char *devname = "/dev/fuse";
char *source = NULL;
char *type = NULL;
struct stat stbuf;
int fd;
int res;
if (!mnt) {
fprintf(stderr, "fuse: missing mountpoint parameter\n");
return -1;
}
res = stat(mnt, &stbuf);
if (res == -1) {
fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
mnt, strerror(errno));
return -1;
}
if (mo->auto_unmount) {
/* Tell the caller to fallback to fusermount because
auto-unmount does not work otherwise. */
return -2;
}
fd = open(devname, O_RDWR);
if (fd == -1) {
if (errno == ENODEV || errno == ENOENT)
fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
else
fprintf(stderr, "fuse: failed to open %s: %s\n",
devname, strerror(errno));
return -1;
}
snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
if (res == -1)
goto out_close;
source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
(mo->subtype ? strlen(mo->subtype) : 0) +
strlen(devname) + 32);
type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
if (!type || !source) {
fprintf(stderr, "fuse: failed to allocate memory\n");
goto out_close;
}
strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
if (mo->subtype) {
strcat(type, ".");
strcat(type, mo->subtype);
}
strcpy(source,
mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
if (res == -1 && errno == ENODEV && mo->subtype) {
/* Probably missing subtype support */
strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
if (mo->fsname) {
if (!mo->blkdev)
sprintf(source, "%s#%s", mo->subtype,
mo->fsname);
} else {
strcpy(source, type);
}
res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
}
if (res == -1) {
/*
* Maybe kernel doesn't support unprivileged mounts, in this
* case try falling back to fusermount
*/
if (errno == EPERM) {
res = -2;
} else {
int errno_save = errno;
if (mo->blkdev && errno == ENODEV &&
!fuse_mnt_check_fuseblk())
fprintf(stderr,
"fuse: 'fuseblk' support missing\n");
else
fprintf(stderr, "fuse: mount failed: %s\n",
strerror(errno_save));
}
goto out_close;
}
#ifndef __NetBSD__
#ifndef IGNORE_MTAB
if (geteuid() == 0) {
char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
res = -1;
if (!newmnt)
goto out_umount;
res = fuse_mnt_add_mount("fuse", source, newmnt, type,
mnt_opts);
free(newmnt);
if (res == -1)
goto out_umount;
}
#endif /* IGNORE_MTAB */
#endif /* __NetBSD__ */
free(type);
free(source);
return fd;
out_umount:
umount2(mnt, 2); /* lazy umount */
out_close:
free(type);
free(source);
close(fd);
return res;
}
static int get_mnt_flag_opts(char **mnt_optsp, int flags)
{
int i;
if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
return -1;
for (i = 0; mount_flags[i].opt != NULL; i++) {
if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
return -1;
}
return 0;
}
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
{
struct mount_opts mo;
int res = -1;
char *mnt_opts = NULL;
memset(&mo, 0, sizeof(mo));
mo.flags = MS_NOSUID | MS_NODEV;
if (args &&
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
return -1;
res = 0;
if (mo.ishelp)
goto out;
res = -1;
if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
goto out;
if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
goto out;
if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
goto out;
res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
if (res == -2) {
if (mo.fusermount_opts &&
fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
goto out;
if (mo.subtype) {
char *tmp_opts = NULL;
res = -1;
if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
free(tmp_opts);
goto out;
}
res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
free(tmp_opts);
if (res == -1)
res = fuse_mount_fusermount(mountpoint, &mo,
mnt_opts, 0);
} else {
res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
}
}
out:
free(mnt_opts);
free(mo.fsname);
free(mo.subtype);
free(mo.fusermount_opts);
free(mo.subtype_opt);
free(mo.kernel_opts);
free(mo.mtab_opts);
return res;
}
-19
View File
@@ -1,19 +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.
*/
#pragma once
#include <sys/types.h>
int fuse_mnt_add_mount(const char *progname, const char *fsname,
const char *mnt, const char *type, const char *opts);
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_fuseblk(void);
-60
View File
@@ -1,60 +0,0 @@
#include "node.hpp"
#include "lfmp.h"
static lfmp_t g_NODE_FMP;
static
__attribute__((constructor))
void
node_constructor()
{
lfmp_init(&g_NODE_FMP,sizeof(node_t),256);
}
static
__attribute__((destructor))
void
node_destructor()
{
lfmp_destroy(&g_NODE_FMP);
}
node_t*
node_alloc()
{
return (node_t*)lfmp_calloc(&g_NODE_FMP);
}
void
node_free(node_t *node_)
{
lfmp_free(&g_NODE_FMP,node_);
}
int
node_gc1()
{
return lfmp_gc(&g_NODE_FMP);
}
void
node_gc()
{
int rv;
int fails;
fails = 0;
do
{
rv = node_gc1();
if(rv == 0)
fails++;
} while(rv || (fails < 3));
}
lfmp_t*
node_lfmp()
{
return &g_NODE_FMP;
}
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include "cpu.hpp"
#include <string>
namespace PinThreads
{
void R1L(const CPU::ThreadIdVec threads);
void R1P(const CPU::ThreadIdVec threads);
void RP1L(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void RP1P(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void R1LP1L(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void R1PP1P(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void RPSL(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void RPSP(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void R1PPSP(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads);
void pin(const CPU::ThreadIdVec read_threads,
const CPU::ThreadIdVec process_threads,
const std::string type);
}
File diff suppressed because it is too large Load Diff
-231
View File
@@ -1,231 +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 GPL.
See the file COPYING.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
static char *progname;
static char *xstrdup(const char *s)
{
char *t = strdup(s);
if (!t) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
exit(1);
}
return t;
}
static void *xrealloc(void *oldptr, size_t size)
{
void *ptr = realloc(oldptr, size);
if (!ptr) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
exit(1);
}
return ptr;
}
static void add_arg(char **cmdp, const char *opt)
{
size_t optlen = strlen(opt);
size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
char *s;
s = cmd + cmdlen;
if (*cmdp)
*s++ = ' ';
*s++ = '\'';
for (; *opt; opt++) {
if (*opt == '\'') {
*s++ = '\'';
*s++ = '\\';
*s++ = '\'';
*s++ = '\'';
} else
*s++ = *opt;
}
*s++ = '\'';
*s = '\0';
*cmdp = cmd;
}
static char *add_option(const char *opt, char *options)
{
int oldlen = options ? strlen(options) : 0;
options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
if (!oldlen)
strcpy(options, opt);
else {
strcat(options, ",");
strcat(options, opt);
}
return options;
}
int main(int argc, char *argv[])
{
char *type = NULL;
char *source;
const char *mountpoint;
char *basename;
char *options = NULL;
char *command = NULL;
char *setuid = NULL;
int i;
int dev = 1;
int suid = 1;
progname = argv[0];
basename = strrchr(argv[0], '/');
if (basename)
basename++;
else
basename = argv[0];
type = "mergerfs";
if (strncmp(basename, "mount.fuse.", 11) == 0)
type = basename + 11;
if (strncmp(basename, "mount.fuseblk.", 14) == 0)
type = basename + 14;
if (type && !type[0])
type = NULL;
if (argc < 3) {
fprintf(stderr,
"usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
progname, type ? "source" : "type#[source]");
exit(1);
}
source = argv[1];
if (!source[0])
source = NULL;
mountpoint = argv[2];
for (i = 3; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
continue;
} else if (strcmp(argv[i], "-t") == 0) {
i++;
if (i == argc) {
fprintf(stderr,
"%s: missing argument to option '-t'\n",
progname);
exit(1);
}
type = argv[i];
if (strncmp(type, "fuse.", 5) == 0)
type += 5;
else if (strncmp(type, "fuseblk.", 8) == 0)
type += 8;
if (!type[0]) {
fprintf(stderr,
"%s: empty type given as argument to option '-t'\n",
progname);
exit(1);
}
} else if (strcmp(argv[i], "-o") == 0) {
char *opts;
char *opt;
i++;
if (i == argc)
break;
opts = xstrdup(argv[i]);
opt = strtok(opts, ",");
while (opt) {
int j;
int ignore = 0;
const char *ignore_opts[] = { "",
"user",
"nouser",
"users",
"auto",
"noauto",
"_netdev",
NULL};
if (strncmp(opt, "setuid=", 7) == 0) {
setuid = xstrdup(opt + 7);
ignore = 1;
}
for (j = 0; ignore_opts[j]; j++)
if (strcmp(opt, ignore_opts[j]) == 0)
ignore = 1;
if (!ignore) {
if (strcmp(opt, "nodev") == 0)
dev = 0;
else if (strcmp(opt, "nosuid") == 0)
suid = 0;
options = add_option(opt, options);
}
opt = strtok(NULL, ",");
}
}
}
if (dev)
options = add_option("dev", options);
if (suid)
options = add_option("suid", options);
if (!type) {
if (source) {
type = xstrdup(source);
source = strchr(type, '#');
if (source)
*source++ = '\0';
if (!type[0]) {
fprintf(stderr, "%s: empty filesystem type\n",
progname);
exit(1);
}
} else {
fprintf(stderr, "%s: empty source\n", progname);
exit(1);
}
}
add_arg(&command, type);
if (source)
add_arg(&command, source);
add_arg(&command, mountpoint);
if (options) {
add_arg(&command, "-o");
add_arg(&command, options);
}
if (setuid && setuid[0]) {
char *sucommand = command;
command = NULL;
add_arg(&command, "su");
add_arg(&command, "-");
add_arg(&command, setuid);
add_arg(&command, "-c");
add_arg(&command, sucommand);
} else if (!getenv("HOME")) {
/* Hack to make filesystems work in the boot environment */
setenv("HOME", "/root", 0);
}
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
strerror(errno));
return 1;
}
-330
View File
@@ -1,330 +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 "mount_util.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#ifndef __NetBSD__
#include <mntent.h>
#endif
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/param.h>
#ifdef __NetBSD__
#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
#define mtab_needs_update(mnt) 0
#else
static int mtab_needs_update(const char *mnt)
{
int res;
struct stat stbuf;
/* If mtab is within new mount, don't touch it */
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
_PATH_MOUNTED[strlen(mnt)] == '/')
return 0;
/*
* Skip mtab update if /etc/mtab:
*
* - doesn't exist,
* - is a symlink,
* - is on a read-only filesystem.
*/
res = lstat(_PATH_MOUNTED, &stbuf);
if (res == -1) {
if (errno == ENOENT)
return 0;
} else {
uid_t ruid;
int err;
if (S_ISLNK(stbuf.st_mode))
return 0;
ruid = getuid();
if (ruid != 0)
setreuid(0, -1);
res = access(_PATH_MOUNTED, W_OK);
err = (res == -1) ? errno : 0;
if (ruid != 0)
setreuid(ruid, -1);
if (err == EROFS)
return 0;
}
return 1;
}
#endif /* __NetBSD__ */
static int add_mount(const char *progname, const char *fsname,
const char *mnt, const char *type, const char *opts)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
if (status != 0)
res = -1;
out_restore:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
}
int fuse_mnt_add_mount(const char *progname, const char *fsname,
const char *mnt, const char *type, const char *opts)
{
if (!mtab_needs_update(mnt))
return 0;
return add_mount(progname, fsname, mnt, type, opts);
}
static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
if (lazy) {
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
"-l", NULL, &env);
} else {
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
NULL, &env);
}
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
if (status != 0) {
res = -1;
}
out_restore:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
}
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
const char *rel_mnt, int lazy)
{
int res;
if (!mtab_needs_update(abs_mnt)) {
res = umount2(rel_mnt, lazy ? 2 : 0);
if (res == -1)
fprintf(stderr, "%s: failed to unmount %s: %s\n",
progname, abs_mnt, strerror(errno));
return res;
}
return exec_umount(progname, rel_mnt, lazy);
}
static int remove_mount(const char *progname, const char *mnt)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
sigemptyset(&blockmask);
sigaddset(&blockmask, SIGCHLD);
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
return -1;
}
res = fork();
if (res == -1) {
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
goto out_restore;
}
if (res == 0) {
char *env = NULL;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
setuid(geteuid());
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
"--fake", mnt, NULL, &env);
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
progname, strerror(errno));
exit(1);
}
res = waitpid(res, &status, 0);
if (res == -1)
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
if (status != 0)
res = -1;
out_restore:
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return res;
}
int fuse_mnt_remove_mount(const char *progname, const char *mnt)
{
if (!mtab_needs_update(mnt))
return 0;
return remove_mount(progname, mnt);
}
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
{
char buf[PATH_MAX];
char *copy;
char *dst;
char *end;
char *lastcomp;
const char *toresolv;
if (!orig[0]) {
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
orig);
return NULL;
}
copy = strdup(orig);
if (copy == NULL) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return NULL;
}
toresolv = copy;
lastcomp = NULL;
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
if (end[0] != '/') {
char *tmp;
end[1] = '\0';
tmp = strrchr(copy, '/');
if (tmp == NULL) {
lastcomp = copy;
toresolv = ".";
} else {
lastcomp = tmp + 1;
if (tmp == copy)
toresolv = "/";
}
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
lastcomp = NULL;
toresolv = copy;
}
else if (tmp)
tmp[0] = '\0';
}
if (realpath(toresolv, buf) == NULL) {
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
strerror(errno));
free(copy);
return NULL;
}
if (lastcomp == NULL)
dst = strdup(buf);
else {
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
if (dst) {
unsigned buflen = strlen(buf);
if (buflen && buf[buflen-1] == '/')
sprintf(dst, "%s%s", buf, lastcomp);
else
sprintf(dst, "%s/%s", buf, lastcomp);
}
}
free(copy);
if (dst == NULL)
fprintf(stderr, "%s: failed to allocate memory\n", progname);
return dst;
}
int fuse_mnt_check_fuseblk(void)
{
char buf[256];
FILE *f = fopen("/proc/filesystems", "r");
if (!f)
return 1;
while (fgets(buf, sizeof(buf), f))
if (strstr(buf, "fuseblk\n")) {
fclose(f);
return 1;
}
fclose(f);
return 0;
}
+6 -6
View File
@@ -12,16 +12,16 @@ is a FUSE-based union filesystem geared towards simplifying storage and manageme
and
.BR aufs .
.SH QUICKSTART
.UR https://trapexit.github.io/mergerfs/quickstart
.LI https://trapexit.github.io/mergerfs/quickstart
.UR https://trapexit.github.io/mergerfs/latest/quickstart
.LI https://trapexit.github.io/mergerfs/latest/quickstart
.UE
.SH DOCUMENTATION
.UR https://trapexit.github.io/mergerfs
.LI https://trapexit.github.io/mergerfs
.UE
.SH SUPPORT
.UR https://trapexit.github.io/mergerfs/support
.LI https://trapexit.github.io/mergerfs/support
.UR https://trapexit.github.io/mergerfs/latest/support
.LI https://trapexit.github.io/mergerfs/latest/support
.UE
.SH FEATURES
.IP \[bu] 2
@@ -74,8 +74,8 @@ When a file or directory is created, a policy is first run to determine which br
For functions which change attributes or remove the file, the behavior may be applied to all instances found.
Read more about policies here:
.UR https://trapexit.github.io/mergerfs/config/functions_categories_and_policies
.LI https://trapexit.github.io/mergerfs/config/functions_categories_and_policies
.UR https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies
.LI https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies
.UE
.SH VISUALIZATION
.nf
+1 -1
View File
@@ -4,4 +4,4 @@ set -e
python3 -m venv venv
. ./venv/bin/activate
pip3 install --no-cache-dir mkdocs mkdocs-material pymdown-extensions mike
pip3 install --no-cache-dir 'mkdocs<2.0' mkdocs-material pymdown-extensions mike
+5 -5
View File
@@ -58,13 +58,13 @@ read/writes. Contact the software author or worse case the mergerfs
author so it may be investigated further.
### write benchmark
### Write Benchmark
```
$ dd if=/dev/zero of=/mnt/mergerfs/16GB.file bs=1M count=16384 oflag=nocache conv=fdatasync status=progress
```
### read benchmark
### Read Benchmark
```
$ dd if=/mnt/mergerfs/16GB.file of=/dev/null bs=1M iflag=nocache conv=fdatasync status=progress
@@ -79,21 +79,21 @@ Below are some basic tests but also try setting `--direct=0`, changing
the `ioengine`, or playing with other options.
#### write
#### Write
```
$ fio --name=writetest --filename=/mnt/mergerfs/16GB.file --size=16Gb --rw=write --bs=1M --direct=1 --numjobs=1 --iodepth=8 --group_reporting --runtime=60 --startdelay=0 --ioengine=psync
```
#### read
#### Read
```
$ fio --name=readtest --filename=/mnt/mergerfs/16GB.file --size=16Gb --rw=read --bs=1M --direct=1 --numjobs=1 --iodepth=8 --group_reporting --runtime=60 --startdelay=0 --ioengine=psync
```
### other benchmarks
### Other Benchmarks
If you are attempting to benchmark other behaviors you must ensure you
clear kernel caches before runs. In fact it would be a good deal to
+8 -8
View File
@@ -41,10 +41,10 @@ transparently enable page caching when mmap is requested. This means
it should be safe to set `cache.files=off`. However, on Linux v6.5 and
below you will need to configure `cache.files` as you need.
If [passthrough](passthrough.md) is enabled so must be page
caching. mergerfs will set `cache.files=auto-full` if `passthrough` is
enabled. And when using `passthrough` the there is no double page
caching since it is in fact passing through the IO.
If [passthrough.io](passthrough.md) is enabled so must page
caching. mergerfs will set `cache.files=auto-full` if `passthrough.io`
is enabled. And when using `passthrough.io` the there is no double
page caching since it is in fact passing through the IO.
[^1]: This is not unique to mergerfs and affects all FUSE
@@ -73,9 +73,9 @@ little risk in setting the value much higher. Especially if there are
no out-of-band changes.
## cache.negative_entry
## cache.negative-entry
* `cache.negative_entry=UINT`: Sets the number of seconds to cache
* `cache.negative-entry=UINT`: Sets the number of seconds to cache
negative entry queries. Defaults to `1`.
This is a cache for negative entry query responses. Such as when a
@@ -172,8 +172,8 @@ the FUSE writeback cache small writes *may* be aggregated by the
kernel and then sent to mergerfs as one larger request. 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 fuse_msg_size section for more
details.
size of a FUSE message. Read the [fuse-msg-size](fuse-msg-size.md)
section for more details.
There is a side effect as a result of enabling writeback
caching. Underlying files won't ever be opened with O_APPEND or
+17 -14
View File
@@ -3,19 +3,22 @@
These are old, deprecated options which may no longer have any
function or have been replaced. **They should not be used.**
* **direct_io**: Bypass page cache. Use `cache.files=off`
instead.
* **kernel_cache**: Do not invalidate data cache on file open. Use
`cache.files=full` instead.
* **auto_cache**: Invalidate data cache if file mtime or
size change. Use `cache.files=auto-full` instead. (default: false)
* **async_read**: Perform reads asynchronously. Use
`async_read=true` instead.
* **sync_read**: Perform reads synchronously. Use
`async_read=false` instead.
* **splice_read**: Does nothing.
* **splice_write**: Does nothing.
* **splice_move**: Does nothing.
* **allow_other**: mergerfs v2.35.0 and above sets this FUSE option
automatically if running as root.
* **use_ino**: Effectively replaced with `inodecalc`.
* **async_read**: Use `async-read=true`.
* **atomic_o_trunc**: Does nothing.
* **attr_timeout**: Use [cache.attr](cache.md).
* **auto_cache**: Use [cache.files=auto-full](cache.md).
* **big_writes**: Does nothing.
* **direct_io**: Use [cache.files=off](cache.md).
* **entry_timeout**: Use [cache.entry](cache.md).
* **hard_remove**: Does nothing.
* **kernel_cache**: Use [cache.files=full](cache.md).
* **negative_entry**: Use [cache.negative-entry](cache.md).
* **noforget**: Use `never-forget-nodes`.
* **nonempty**: Does nothing.
* **splice_move**: Does nothing.
* **splice_read**: Does nothing.
* **splice_write**: Does nothing.
* **sync_read**: Use `async-read=false`.
* **use_ino**: Use [inodecalc](inodecalc.md).
+25 -5
View File
@@ -1,10 +1,10 @@
# func.readdir
Defaults to `seq`
* type: `STR`
* default: `seq`
* examples: `func.readdir=seq`, `func.readdir=cor:4`
Examples: `func.readdir=seq`, `func.readdir=cor:4`
`readdir` has policies to control how it reads directory content.
`readdir` has policies to control how to read directory content.
| Policy | Description |
| ------ | ----------- |
@@ -12,7 +12,7 @@ Examples: `func.readdir=seq`, `func.readdir=cor:4`
| cosr:N:M | "concurrent open, sequential read" : Concurrently open branch directories using a thread pool and process them in the order defined in `branches`. This keeps memory and CPU usage low while also reducing the time spent waiting on branches to respond. `N` is the number of threads. If negative it will be the core count divided by `abs(N)`. `M` is the queue depth. If either value is `0` it will be decided based on system configuration. |
| cosr:N | cosr:N:M with M = 0 |
| cosr | cosr:N:M with N = 0 and M = 0 |
| cor:N:M | "concurrent open and read" : Concurrently open branch directories and immediately start reading their contents using a thread pool. This will result in slightly higher memory and CPU usage but reduced latency. Particularly when using higher latency / slower speed network filesystem branches. Unlike `seq` and `cosr` the order of files could change due the async nature of the thread pool. This should not be a problem since the order of files listed in not guaranteed. `N` is the number of threads. If negative it will be the core count divided by `abs(N)`. `M` is the queue depth. If either value is `0` it will be decided based on system configuration. |
| cor:N:M | "concurrent open and read" : Concurrently open branch directories and immediately start reading their contents using a thread pool. This will result in slightly higher memory and CPU usage but reduced latency. Particularly when using higher latency / slower speed network filesystem branches. Unlike `seq` and `cosr` the order of files could change due the async nature of the thread pool. This should not be a problem since the order of files returned in not guaranteed. `N` is the number of threads. If negative it will be the core count divided by `abs(N)`. `M` is the queue depth. If either value is `0` it will be decided based on system configuration. |
| cor:N | cosr:N:M with M = 0 |
| cor | cosr:N:M with N = 0 and M = 0 |
@@ -21,3 +21,23 @@ in a directory and possibly some basic metadata about said files. To
know details about the files, as one would see from commands like
`find` or `ls`, it is required to call `stat` on the file which is
controlled by `fuse.getattr`.
Until Linux v6.16, despite being able to change the size of FUSE
messages, `readdir` requests were limited to 1 page (4KiB) in size per
message. This limitation limited throughput and therefore performance
of the `readdir` call. In v6.16 that was changed to be able to grow to
the [fuse-msg-size](fuse-msg-size.md) which allows a lot more data to
be sent per request and therefore much better performance in certain
situations.
## Technical Details
* On Linux
[getdents](https://man7.org/linux/man-pages/man2/getdents.2.html) is
used instead of
[readdir](https://man7.org/linux/man-pages/man2/readdir.2.html) in
order to leverage larger buffers for better performance on larger
directory reads as well as have more control in concurrent policies.
* [readdir](https://man7.org/linux/man-pages/man2/readdir.2.html) is
used on FreeBSD.
@@ -5,7 +5,9 @@ The POSIX filesystem API is made up of a number of functions. `creat`,
the core functions are grouped into 3 categories: `action`, `create`,
and `search`. These functions and categories can be assigned a policy
which dictates which branch is chosen when performing that
function.
function. IE. When an application requests that a file or directory is
created how should mergerfs decide which branch to choose to attempt
the create? That is the policy.
Some functions, listed in the category `N/A` below, can not be
assigned the normal policies because they are directly related to a
@@ -44,9 +46,9 @@ branch base path provided is used. Not the full path to the file or
directory in question. Meaning that mounts within the branch will not
be considered in the space calculations.
NOTE: While any policy can be assigned to a function or category, some
may not be very useful in practice. For instance: `rand` (random) may
be useful for file creation but could lead to very odd behavior if
**NOTE:** While any policy can be assigned to a function or category,
some may not be very useful in practice. For instance: `rand` (random)
may be useful for file creation but could lead to very odd behavior if
used for `chmod` if there were more than one copy of the file. Unless
users find this flexibility useful it will likely be removed in the
future.
@@ -69,7 +71,7 @@ whether or not a branch is mounted read-only, and the branch mode
[minfreespace](minfreespace.md).
Policies may have their own additional filtering such as those that
require existing paths to be present.
require relative 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
@@ -114,6 +116,7 @@ but it makes things a bit more uniform.
| mfs (most free space) | Pick the branch with the most available free space. |
| ff (first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Pick the branch with the least available free space. |
| lup (least used percent) | Pick the branch with the least used percentage of space |
| lus (least used space) | Pick the branch with the least used space. |
| all | Search: For **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| msppfrd (most shared path, percentage free random distribution) | Like **eppfrd** but if it fails to find a branch it will try again with the parent directory. Continues this pattern till finding one. |
@@ -1,13 +1,12 @@
# fuse_msg_size
# fuse-msg-size
* `fuse_msg_size=UINT|SIZE`
* Defaults to `1M`
* Performance improvements often peak at about `4M`
* type: `UINT|SIZE`
* default: `1M`
* example: `fuse-msg-size=1M`
* If the value is a `SIZE` in the form of `xB`, `xK`, `xM`, `xG`, or
`xT` then it will choose the closest multiple of the system page
size
* If the value is just an integer without a size multipler suffix then
it is treated as a page count
`xT` then it will choose the closest multiple of the system page
size. If the value is just an integer without a size multipler suffix
then it is treated as a page count
FUSE applications communicate with the kernel over a special character
device: `/dev/fuse`. A large portion of the overhead associated with
@@ -29,10 +28,10 @@ v6.13](https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/co
and above the max value supported by the kernel can range from 1
(4KiB) to 65535 (~256MiB) (assuming a page size of 4KiB.) The default
used by Linux >= 4.20, and hard coded value used before 4.20, is 32
(128KiB). In mergerfs it is referred to as `fuse_msg_size` to make it
(128KiB). In mergerfs it is referred to as `fuse-msg-size` to make it
clear what it impacts and provide some abstraction.
If the `fuse_msg_size` value provided is more than the system wide
If the `fuse-msg-size` value provided is more than the system wide
maximum mergerfs will attempt to increase the system wide value to keep
the user from needing to set the value using `sysctl`,
`/etc/sysctl.conf`, or `/proc/sys/fs/fuse/max_pages_limit`.
@@ -48,4 +47,4 @@ v4.20 and v6.13 the max value is 256. On kernels >= v6.13 the maximum
value is 65535.
NOTE: If you intend to enable `cache.files` you should also set
[readahead](readahead.md) to match `fuse_msg_size`.
[readahead](readahead.md) to match `fuse-msg-size`.
@@ -16,4 +16,4 @@ help. Like [export-support](export-support.md) this is mostly for
debugging.
This option is a kernel mount option so unable to be changed at
runtime.
runtime. It controls the `default_permissions` FUSE kernel option.
@@ -0,0 +1,27 @@
# lazy-umount-mountpoint
* type: `BOOL`
* default: `false`
* example: `lazy-umount-mountpoint=true`
When enabled mergerfs will issue a [lazy
umount](https://man7.org/linux/man-pages/man8/umount.8.html) request
to the mount point as it starts.
```
Lazy unmount. Detach the filesystem from the file hierarchy
now, and clean up all references to this filesystem as soon as
it is not busy anymore.
```
The value of using this is in situations where you wish to change
options which can only be set at initialization or upgrade mergerfs
itself. It also helps in the case of using `mount -a` which leads to a
new instance of mergerfs being mounted due to it being unable to
recognize that it is already mounted.
In theory mergerfs could notice that an instance is already mounted at
the mount point and attempt to use the runtime interface to modify the
options and only mounting a new instance if the version has changed or
initialization time only options are changed. However, given the
relative rarity of this being an issue it isn't a priority.
@@ -1,6 +1,6 @@
# link_cow
# link-cow
* `link_cow=true|false`
* `link-cow=true|false`
* Defaults to `false`
This feature offers similar functionality to what
+1 -1
View File
@@ -30,7 +30,7 @@ as appropriate and the original error returned.
If `moveonenospc` is disabled the underlying error will be returned.
NOTE: This feature has NO affect on policies. It ONLY applies to the
NOTE: This feature has NO effect on policies. It ONLY applies to the
literal write function. If the create function returns `ENOSPC` or the
[policy returns `ENOSPC`](functions_categories_policies.md#filtering)
that error will be returned back to the application making the
+42 -27
View File
@@ -30,9 +30,11 @@ config file.
## mount options
* **config**: Path to a config file. Same arguments as below in
key=val / ini style format.
`key` or `key=val` style format. `#` used for comments.
* **[branches](branches.md)**: Colon delimited list of branches. Used
primarily in config file.
primarily in a config file.
* **mountpoint=DIRPATH**: The path to the mountpoint. Used primarily
in a config file.
* **[branches-mount-timeout](branches-mount-timeout.md)=UINT**: Number
of seconds to wait at startup for branches to be a mount other than
the mountpoint's filesystem. (default: 0)
@@ -68,7 +70,7 @@ config file.
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](symlinkify.md)=UINT**: Time to wait, in
* **[symlinkify-timeout](symlinkify.md)=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
@@ -86,13 +88,16 @@ config file.
* **[export-support](export-support.md)=BOOL**: Sets a low-level FUSE
feature intended to indicate the filesystem can support being
exported via NFS. (default: true)
* **security_capability=BOOL**: If false return ENOATTR when xattr
* **[kernel-permissions-check](kernel-permissions-check.md)=BOOL**:
Controls the FUSE `default_permissions` option. Primarily for
debugging. (default: true)
* **security-capability=BOOL**: If false return ENOATTR when xattr
security.capability is queried. (default: true)
* **[xattr](xattr.md)=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](link_cow.md)=BOOL**: When enabled if a regular file is
* **[link-cow](link-cow.md)=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
@@ -101,7 +106,7 @@ config file.
means it will always use all branches in statfs calculations. 'full'
is in effect path preserving and only includes branches where the
path exists. (default: base)
* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to
* **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)
@@ -118,20 +123,20 @@ config file.
directory and symlink to it.
* **[readahead](readahead.md)=UINT**: Set readahead (in kilobytes) for
mergerfs and branches if greater than 0. (default: 0)
* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel
* **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
* **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](fuse_msg_size.md)=PAGESIZE**: Set the max number of
* **[fuse-msg-size](fuse-msg-size.md)=PAGESIZE**: Set the max number of
pages per FUSE message. Only available on Linux >= 4.20 and ignored
otherwise. (min: 1; max: 65535; default: "1M")
* **[threads](threads.md)=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
* **[read-thread-count](threads.md)=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
@@ -139,7 +144,6 @@ config file.
`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](threads.md)=INT**: Alias for `threads`.
* **[process-thread-count](threads.md)=INT**: Enables separate thread
pool to asynchronously process FUSE requests. In this mode
`read-thread-count` refers to the number of threads reading FUSE
@@ -150,7 +154,7 @@ config file.
time. Meaning the total memory usage of the queues is queue depth
multiplied by the number of process threads plus read thread
count. 0 sets the depth to the same as the process thread
count. (default: 0)
count. (default: 2)
* **[pin-threads](pin-threads.md)=STR**: Selects a strategy to pin
threads to CPUs (default: unset)
* **[flush-on-close](flush-on-close.md)=never|always|opened-for-write**:
@@ -159,15 +163,17 @@ config file.
* **scheduling-priority=INT**: Set mergerfs' scheduling
priority. Valid values range from -20 to 19. See `setpriority` man
page for more details. (default: -10)
* **[proxy-ioprio](proxy-ioprio.md)=BOOL**: Use ioprio value from
calling threads when reading and writing. (default: false)
* **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](functions_categories_policies.md)=POLICY**: Sets the
specific FUSE function's policy. See below for the list of value
types. Example: **func.getattr=newest**
* **[func.readdir](func_readdir.md)=seq|cosr|cor|cosr:INT|cor:INT**: Sets `readdir`
policy. INT value sets the number of threads to use for
concurrency. (default: seq)
* **[func.readdir](func_readdir.md)=seq|cosr|cor|cosr:INT|cor:INT**:
Sets `readdir` policy. INT value sets the number of threads to use
for concurrency. (default: seq)
* **[category.action](functions_categories_policies.md)=POLICY**: Sets
policy of all FUSE functions in the action category. (default:
epall)
@@ -182,7 +188,7 @@ config file.
timeout in seconds. (default: 1)
* **[cache.entry](cache.md#cacheentry)=UINT**: File name lookup cache
timeout in seconds. (default: 1)
* **[cache.negative_entry](cache.md#cachenegative_entry)=UINT**:
* **[cache.negative-entry](cache.md#cachenegative-entry)=UINT**:
Negative file name lookup cache timeout in seconds. (default: 0)
* **[cache.files](cache.md#cachefiles)=off|partial|full|auto-full|per-process**:
File page caching mode (default: off)
@@ -201,15 +207,24 @@ config file.
with `cache.files=per-process` (if the process is not in
`process-names`) or `cache.files=off`. (Is a kernel feature added in
v6.2) (default: true)
* **[passthrough](passthrough.md)**: Enable [FUSE IO
* **[passthrough.io](passthrough.md)=ENUM**: Enable [FUSE IO
passthrough](https://kernelnewbies.org/Linux_6.9#Faster_FUSE_I.2FO)
if available. (default: off)
* **gid-cache-expire-timeout**: Number of seconds till supplemental
group data is refreshed in the [GID
cache](../known_issues_bugs.md#supplemental-user-groups). (default:
3600)
* **gid-cache-remove-timeout**: Number of seconds to wait till cached
data is removed due to lack of usage. (default: 43200)
* **passthrough.max-stack-depth=INT**: Set to `1` another filesystem
can be stacked on mergerfs. Set to `2` to have mergerfs stacked over
another filesystem. (default: 1)
* **remember-nodes=INT**: The number of seconds to keep the internal
representation of a file once the OS tells mergerfs it is no longer
needed. Really only needed for [exporting mergerfs via
NFS](../remote_filesystems.md) (default: 0)
* **never-forget-nodes=BOOL**: Sets `remember-nodes` to
infinity. Formerly `noforget`. (default: false)
* **allow-idmap=BOOL**: Enables idmap (identity mapping). (default:
true)
* **debug**: Enable debugging information. At the moment limited to a
FUSE message trace saved to `log.file`. (default: false)
* **log.file**: The file path for the FUSE message trace. Empty string
will set to stderr. (default: stderr)
**NOTE:** Options are evaluated in the order listed so if the options
are **func.rmdir=rand,category.action=ff** the **action** category
+28 -29
View File
@@ -1,4 +1,4 @@
# passthrough
# passthrough.io
* default: `off`
* arguments:
@@ -14,7 +14,7 @@ to act as an active proxy for all read and write requests. This
results in, at times, significant overhead compared to direct
interaction with the underlying filesystems. Not because `mergerfs` is
doing anything particularly slow but because the additional
communication and data transfers required. With the `passthrough`
communication and data transfers required. With the `passthrough.io`
feature `mergerfs` is able to instruct the kernel to perform reads and
writes directly on the underlying file. Bypassing `mergerfs` entirely
(specifically and only for reads and writes) and thereby providing
@@ -31,54 +31,53 @@ features related to read/write are affected.
* [readahead](readahead.md): Still affects the `readahead` values of
underlying filesystems and `mergerfs` itself but no longer relevant
to `mergerfs` in that it is not servicing IO requests.
* [fuse_msg_size](fuse_msg_size.md): The primary reason to increase
* [fuse-msg-size](fuse-msg-size.md): The primary reason to increase
the FUSE message size is to allow transferring more data per request
which helps improve read and write performance. Without read/write
requests being sent to `mergerfs` there is little reason to have
larger message sizes since the only other message larger than 1
page, directory reading, currently has a small fixed buffer size.
* parallel-direct-writes: Not only is `direct-io` and `passthrough`
* parallel-direct-writes: Not only is `direct-io` and `passthrough.io`
mutually exclusive but since `mergerfs` no longer receives write
requests in passthrough mode then there is no parallel writes
possible.
* [cache.writeback](cache.md): FUSE's writeback caching is
incompatible with `passthrough`. If `cache.writeback=true` when
enabling `passthrough` it will be reset to `false`.
* [cache.files](cache.md): Must be enabled for `passthrough` to
incompatible with `passthrough.io`. If `cache.writeback=true` when
enabling `passthrough.io` it will be reset to `false`.
* [cache.files](cache.md): Must be enabled for `passthrough.io` to
work. When `cache.files=off` FUSE's `direct-io` mode is enabled
which overrides `passthrough.` Meaning `cache.files` should be set
to `partial`, `full` or `auto-full` to use `passthrough`. If
`cache.files` is set to `off` when using `passthrough` it will
which overrides `passthrough.io`. Meaning `cache.files` should be
set to `partial`, `full` or `auto-full` to use `passthrough.io`. If
`cache.files` is set to `off` when using `passthrough.io` it will
reset it to `auto-full`.
Technically `mergerfs` has the ability to choose options like
`passthrough`, `direct-io`, and page caching independently for every
file opened. However, at the moment there is no use case for picking
and choosing which to enable outside `cache.files=per-process` (which
is largely unnecessary on Linux v6.6 and above. See
`passthrough.io`, `direct-io`, and page caching independently for
every file opened. However, at the moment there is no use case for
picking and choosing which to enable outside `cache.files=per-process`
(which is largely unnecessary on Linux v6.6 and above. See
[direct-io-allow-mmap](options.md)) If such a use case arises please
[reach out to the author](../support.md) to discuss.
Unlike [preload.so](../tooling.md#preloadso), `passthrough` will work for
any software interacting with `mergerfs`. However, `passthrough`
requires Linux v6.9 or above to work.
Unlike [preload.so](../tooling.md#preloadso), `passthrough.io` will
work for any software interacting with `mergerfs`. However,
`passthrough.io` requires Linux v6.9 or above to work.
**NOTE:** This feature will **ONLY** work if `mergerfs` is running as
`root` as currently only `root` is allowed to leverage the kernel
feature.
**NOTE:** If a file has been opened and `passthrough` enabled, while
that file is open, if another open request is made `mergerfs` must
also enable `passthrough` for the second open request. This is a
limitation of how the passthrough feature works. Though there is no
known usecase where this is useful.
**NOTE:** If a file has been opened and `passthrough.io` enabled,
while that file is open, if another open request is made `mergerfs`
must also enable `passthrough.io` for the second open request. This is
a limitation of how the passthrough feature works.
**NOTE:** In order to add `passthrough` feature to `mergerfs` it was
necessary to remove the "feature" where mergerfs could open the same
file on different branches. Such as using `func.open=rand` and having
multiple files at the same relative path across different
**NOTE:** In order to add `passthrough.io` feature to `mergerfs` it
was necessary to remove the "feature" where mergerfs could open the
same file on different branches. Such as using `func.open=rand` and
having multiple files at the same relative path across different
branches. This "feature" was very very rarely used and it was
impossible to support `passthrough` without changing the behavior.
impossible to support `passthrough.io` without changing that behavior.
## Alternatives
@@ -104,7 +103,7 @@ Summary: passthrough is ~95% native speed. 200% faster than
* straight to tmpfs: 1.7 GB/s
* nullrw=true: 2.1 GB/s
* cache.files=off; passthrough=off: 800 MB/s
* cache.files=auto-full; passthrough=rw: 1.6 GB/s
* cache.files=off; passthrough.io=off: 800 MB/s
* cache.files=auto-full; passthrough.io=rw: 1.6 GB/s
* cache.files=auto-full; cache.writeback=false: 518 MB/s
* cache.files=auto-full; cache.writeback=true: 518 MB/s
+53
View File
@@ -0,0 +1,53 @@
# proxy-ioprio
* type: `BOOL`
* default: `false`
* example: `proxy-ioprio=true`
In Linux certain process schedulers have the ability to
[prioritize](https://man7.org/linux/man-pages/man2/ioprio_set.2.html)
based on a per thread [IO
priority](https://www.kernel.org/doc/Documentation/block/ioprio.txt)
assigned to the software by users using tools such as
[ionice](https://man7.org/linux/man-pages/man1/ionice.1.html). Since
such details are not provided by the FUSE protocol, users may not use
schedulers which support IO priority, and the extra overhead to query
the priority and set it within mergerfs it does not by default attempt
to mirror/proxy the value. When enabled however, for all read and
write requests, mergerfs will query the priority of the requesting
process/thread and set the same value on the thread within mergerfs
making the read/write.
This may be useful in situation where the system has high IO load or
processes are known to have varying priorities. See the [man
page](https://man7.org/linux/man-pages/man2/ioprio_set.2.html) for
specific details.
Keep in mind that if no IO scheduler has been set for a thread then by
default the IO priority will match the CPU nice value which for
mergerfs is set by [scheduling-priority](options.md). [Nice
value](https://man7.org/linux/man-pages/man2/getpriority.2.html)
proxying may be added in a future release.
## Conflicts With Other Options
If using [IO passthrough](passthrough.md) there is no reason to set
this value to `true`. However, since `passthrough.io` leads to
mergerfs IO calls being bypassed entirely will have no impact on
performance or behavior regardless.
When using `passthrough.io` the IO priority of the client app would be
used directly given the IO is passed through.
## Supported Platforms
Only Linux. This is not supported on FreeBSD.
## Performance Impact
Despite the additional syscalls requires the impact appears
undetectable with larger buffer sizes. At very small buffer sizes
(such as 512 bytes) there is a noticable but small impact (~1.5%).
+1 -1
View File
@@ -6,7 +6,7 @@ value unit is in kibibytes.
* `readahead=1024`
While the max size of messages sent between the kernel and mergerfs is
configurable via the [fuse_msg_size](fuse_msg_size.md) option that
configurable via the [fuse-msg-size](fuse-msg-size.md) option that
doesn't mean that is the size used by the kernel for read and
writes.
+2 -2
View File
@@ -55,6 +55,6 @@ returned but it will still be possible.
## Additional Reading
* [Do hardlinks work?](../faq/technical_behavior_and_limitations.md#do-hardlinks-work)
* [Do hardlinks work?](../faq/technical_behavior_and_limitations.md#do-hard-links-work)
* [How does mergerfs handle moving and copying of files?](../faq/technical_behavior_and_limitations.md#how-does-mergerfs-handle-moving-and-copying-of-files)
* [Why are file moves and renames failing?](../faq/why_isnt_it_working.md#why-are-file-moves-renames-linkshardlinks-failing)
* [Why are file moves and renames failing?](../faq/why_isnt_it_working.md#why-are-file-moves-renames-linkshard-links-failing)
+12 -5
View File
@@ -88,11 +88,18 @@ filesystems. Mainly `ext4`.
## Logging
Filesystems, and therefore mergerfs, are doing lots of small actions
at high speed. It simply isn't reasonable to log all the actions of
the system. That said: certain details are logged at startup and when
performing maintenance tasks. These are logged via `syslog` and on
`systemd` based systems can be viewed by running
Filesystems, and therefore mergerfs, are doing **lots** of small
actions at high speed. It simply isn't reasonable to log all the
actions of the system all the time. However, there is a debug mode
which can be toggled on as needed, even at runtime, which will record
some information to help with debugging. The main thing it will record
is a trace of all FUSE messages to the location defined by the
`log.file` option. See [config section](config/options.md) for more
details.
That said even with debug mode disabled mergerfs will log certain
details via `syslog` which on `systemd` based systems can be viewed by
running:
```
journalctl -t mergerfs
+11
View File
@@ -68,6 +68,11 @@ passthrough IO or other features.
### time based expiring
**NOTE:** this script is an **example**, not particularly efficient, and
really not intended for production deployments. Until such time that
mergerfs provides tooling for this usecase consider using
[mergerfs-cache-mover](https://github.com/monstermuffin/mergerfs-cache-mover).
Move files from cache filesystem to base pool which have an access
time older than the supplied number of days. Replace `-atime` with
`-amin` in the script if you want minutes rather than days.
@@ -99,6 +104,12 @@ respectively.
### percentage full expiring
**NOTE:** this script is an **example**, not particularly efficient, and
really not intended for production deployments. Until such time that
mergerfs provides tooling for this usecase consider using
[mergerfs-cache-mover](https://github.com/monstermuffin/mergerfs-cache-mover).
While the cache filesystem's percentage full is above the provided
value move the oldest file from the cache filesystem to the base pool.
@@ -5,16 +5,10 @@
[Primarily Linux.](../setup/installation.md) FreeBSD is casually
supported but not well tested.
With FreeBSD certain Linux functions and FUSE features are not
supported. In many cases the absense will not be noticed however
performance may be impacted due to a core mergerfs design decision
relying on a Linux feature.
Linux allows individual threads to change credentials whereas [FreeBSD
does not](https://wiki.freebsd.org/Per-Thread%20Credentials). As a
result mergerfs must use a lock to ensure critical sections which need
to change credentials are safeguarded. This will limit throughput on
systems where requests to mergerfs come from multiple user identities.
With FreeBSD certain Linux functions and FUSE features [are not
supported.](../known_issues_bugs.md#freebsd-version) In many cases the
absence will not be noticed however more advanced features will not be
available.
### Why not support MacOS?
@@ -36,33 +30,90 @@ level API.
Windows, while used for NAS systems more often than MacOS, is still
relatively uncommon when compared to Linux. [Drive
Pool](../project_comparisons.md#stablebits-drivepool) is a reasonable
alternative.
Pool](../project_comparisons.md#stablebits-drivepool) is a good
alternative. It may also be possible to use mergerfs with WSL.
## What filesystems can be used as branches?
ext4, btrfs, xfs, f2fs, zfs, nfs, etc.
On the surface any filesystem should work but there could be issues
with non-POSIX compliant filesystems such as vfat, ntfs, cifs, exfat,
etc. When directories need to be created or files moved by mergerfs if
the filesystem returns errors due to not supporting certain POSIX
filesystem features it could result in the core functions failing.
Most any filesystem should work but there could be issues with
non-POSIX compliant filesystems such as nfs (w/ root squashing), vfat,
ntfs, cifs, exfat, etc. When directories need to be created or files
moved by mergerfs if the filesystem returns errors due to not
supporting certain POSIX filesystem features it could result in the
core functions failing.
Since mergerfs is not generally used with non-POSIX filesystems this
has not been a problem for users and there are some checks for known
edgecases but it is possible some are not accounted for. If use with a
edge cases but it is possible some are not accounted for. If use with a
filesystem results in issues please [file a
ticket](https://github.com/trapexit/mergerfs/issues) with the details.
## What types of storage devices does mergerfs work with?
Since mergerfs works at the filesystem level rather than device level
there is no compatibility concerns based on the physical storage
devices involved.
## Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
[https://www.snapraid.it](https://www.snapraid.it)
Yes. They are completely unrelated pieces of software that just happen
to work well together.
## How does mergerfs interact with SnapRAID recovery behavior?
SnapRAID does not choose parity layout based on directory structure or
relative path. As each file is discovered, SnapRAID fills parity space
for that file based on its size using the next available parity area.
This makes the logical parity layout depend on discovery order, file
size, and how much data already exists on each filesystem at that
time. Filesystems with similar used space, regardless of total
capacity, are more likely to place newly added files into similar
parity positions.
Because of that, if files are changed or removed across multiple
branches and those files happen to overlap in parity space, recovery
of those deleted files may be less reliable. This is not a general
concern, is not unique to mergerfs usage, and it does not affect
filesystem failure recovery. It is specifically a concern when trying
to recover from accidental deletion of files spread across multiple
filesystems.
In practice this mostly matters when the filesystems have very similar
used space, the files are created across those filesystems around the
same time, and you later want SnapRAID to recover an accidental
deletion spanning multiple filesystems. Otherwise this is usually not
something to worry about.
If, however, this is a concern it can be beneficial to keep files on
as few filesystems as possible or otherwise keep related files
together. See the [collocation
section.](configuration_and_policies.md#how-can-i-ensure-files-are-collocated-on-the-same-branch)
## Can I use mergerfs without NonRAID? NonRAID without mergerfs?
[https://github.com/qvr/nonraid](https://github.com/qvr/nonraid)
Yes. They are completely unrelated pieces of software that just happen
to work well together.
### How does mergerfs interact with NonRAID?
There are no known special interactions or concerns between mergerfs
and NonRAID. They are separate tools and mergerfs has no known
behavior which requires any special configuration or caveats when used
with NonRAID.
## Does mergerfs support CoW / copy-on-write / writes to read-only filesystems?
Not in the sense of a filesystem like BTRFS or ZFS nor in the
@@ -80,16 +131,8 @@ pool.
## Can mergerfs run via Docker, Podman, Kubernetes, etc.
Yes. With Docker you'll need to include `--cap-add=SYS_ADMIN
--device=/dev/fuse --security-opt=apparmor:unconfined` or similar with
other container runtimes. You should also be running it as root or
given sufficient caps to allow mergerfs to change user and group
identity as well as have root like filesystem permissions. This
ability is critical to how mergerfs works.
Also, as mentioned by [hotio](https://hotio.dev/containers/mergerfs),
with Docker you should probably be mounting with `bind-propagation`
set to `slave`.
Yes. [See installation
page.](../setup/installation.md#podman-docker-oci-containers)
## How does mergerfs interact with user namespaces?
@@ -108,11 +151,13 @@ given the complication it introduces.
## Can mergerfs be used with NFS root squash?
If mergerfs is pooling a NFS mount then root squash should be disabled
If mergerfs is pooling a NFS mount then root squash must be disabled
as mergerfs needs to be able to have elevated privileges to do what it
does.
does (set permissions, ownership, etc.)
If you are exporting mergerfs over NFS then it is not really necessary.
If you are exporting mergerfs over NFS it is not strictly necessary to
disable root squashing but 3rd party software running as root may
fail.
See the [section on remote filesystems.](../remote_filesystems.md)
+76 -27
View File
@@ -18,37 +18,82 @@ mergerfs [does not impact](usage_and_functionality.md) the underlying
filesystems and can be added or removed without any impact it is
extremely easy to test and experiment with different settings.
Additional reading:
* [Config/Options](../config/options.md)
## What create policy should I use?
If it isn't clear to you then you should use `pfrd` as described in
the [quickstart guide](../quickstart.md).
## Why are there no policies based on file size?
Because it is not possible to know the final size of a file at creation
time.
See [How does mergerfs handle moving and copying of
files?](technical_behavior_and_limitations.md#how-does-mergerfs-handle-moving-and-copying-of-files)
and [Intro to Filesystems - Creating a
file](../intro_to_filesystems.md#creating-a-file).
## Why is pfrd the default create policy?
Originally the default was `epmfs` however it was found to cause
significant confusion among new users who expected mergerfs to always
choose a branch with available space. For mergerfs v2.41.0 it was
decided to change the default to the less restrictive policy `pfrd`.
significant confusion among new users who assumed mergerfs to behave
like other software which always choose a branch with most available
space without any filtering or restrictions. The reason for using
`epmfs` was to allow people to use mergerfs without impacting the
layout of their data across the branches. Keeping the total entropy of
the system in check. This, however, was not understood by users who
did not read the documentation and caused significant support burdens.
For mergerfs v2.41.0 it was decided to change the default to `pfrd` to
minimize the support burden.
`pfrd` was chosen because it prioritizes placement to branches based
on free space (percentage wise) without overloading a specific branch
as `mfs`, `lus`, or other policies could when a singular branch has
significantly more free space. Since it is impossible to know the
usage and access patterns of the filesystem spreading out files across
the branches also spreads the load and therefore good for general use
cases making it a good default.
Additional reading:
* [functions, categories and policies](../config/functions_categories_policies.md)
## How can I ensure files are collocated on the same branch?
Many people like the idea of ensuring related files, such as all the
files to a TV show season or songs in an album, are stored on the same
storage device. However, most people have no actual need for this
storage device. However, most people have no technical need for this
behavior.
1. If you backup your data it is extremely likely your backup solution
can restore only those files you are missing.
2. Software such as **Sonarr** can manage the downloading and post
processing of bespoke episodes which may be missing in a
season. Either by downloading the episode individually if available
or by downloading a full season.
can restore only those files you are missing. There is no need to
store data in related units to ensure restoring data is easy.
2. Software such as [Sonarr](https://sonarr.tv) can manage the
downloading and post processing of bespoke files / episodes which
may be missing. Either by downloading the episode individually if
available or by downloading a full season.
3. There is no benefit to keeping files collocated with regard to
drive spinup, caching, or other secondary concern.
drive spinup, caching, or other secondary concern. In fact
depending on usage patterns placing the data on the same underlying
device could harm performance.
The main use case for wanting collocation is where the branch is going
to be removed from the pool and you wish to have all data from some
logical set on that device. Such as you intend to take a drive out of
the pool to take on a trip and want a whole show on the
drive. However, even in these situations you typically end up needing
to curate the files anyway because it has show A but not show B.
to be removed from the pool and used in isolation temporarily and you
wish to have all data from some logical set on that device. Such as
you intend to take a drive out of the pool to take on a trip and want
a whole show or artist's works on the drive. However, even in these
situations you typically end up needing to curate the files anyway
because it has show A but not show B. In such cases it is more
convient to use a separate devices and sync what you wish to it.
All that said you can accomplish collocation to varying degrees using
the following methods:
@@ -56,33 +101,37 @@ the following methods:
1. Use
[mergerfs.consolidate](https://github.com/trapexit/mergerfs-tools/blob/master/src/mergerfs.consolidate)
when consolidation is needed.
2. Use a `msp` create policy.
2. Use a `msp` create policy to do a best effort collocation without
needing explicit directory curation.
3. Use `epmfs` or other `ep` create policy and manually create paths
on the branches directly.
4. Use a `ep` `create` policy and `rand` for `mkdir`.
on the branches directly as you need/desire.
4. Use a `ep` `create` policy and `pfrd` or `rand` for `mkdir`.
## How can I balance files across the pool?
Similar to collocation there is generally little reason to balance
files.
files across the pool branches.
1. Since prediction of a filesystem's death or loss of data is near
impossible there is little reason to balance in hopes of limiting
data loss.
1. Since prediction of a device's/filesystem's death or loss of data
is near impossible there is little reason to balance in hopes of
limiting data loss.
2. While performance could be impacted by having too much reading or
writing happen to singular underlying filesystems balancing won't
help unless you have the ability to manage the access patterns to
the pool.
3. Over time most configurations will lead to a random distribution of
files across the branches which is effectively "balancing."
3. Over time the churn of adding and removing files will lead to a
random distribution of files across the branches which is
effectively "balancing" if using create policies which do not favor
any particular branch.
If you wish to move files around or balance the pool you can:
All that said if you wish to move files around or balance the pool you
can:
1. Use `rand` or `pfrd` create policies and just use your system as
normal.
2. Write simple scripts using rsync or similar to move files around as
you wish.
2. Write scripts using rsync or similar to move files around as you
wish.
3. Use
[mergerfs.balance](https://github.com/trapexit/mergerfs-tools/blob/master/src/mergerfs.balance). Keep
in mind that this tool is really just an example of how to
+109 -34
View File
@@ -1,22 +1,31 @@
# Limiting drive spinup
## Does mergerfs do anything to prevent drive spinup?
No. In fact it makes it worse.
## How can I setup my system to limit drive spinup?
TL;DR: You really can't. Not through mergerfs alone. In fact mergerfs
makes an attempt to do so more complicated.
TL;DR: You really can't. Not through mergerfs alone. In fact using
mergerfs makes any general attempt to limit spinup more complicated.
mergerfs is a proxy. Despite having some caching behaviors it is not
designed to cache much more than metadata. It proxies calls between
client software and underlying filesystems. If a client makes a
request such as `open`, `readdir`, `stat`, etc. it must translate that
into something that makes sense across multiple filesystems. For
`readdir` that means running the call against all branches and
aggregating the output. For `open` that means finding the file to open
and doing so. The only way to find the file to open is to scan across
all branches and sort the results and pick one. There is no practical
way to do otherwise. Especially given so many mergerfs users expect
out-of-band changes to "just work."
mergerfs is a proxy. Despite having [some caching
behaviors](../config/cache.md) it is not designed to cache much more
than metadata. It proxies calls between client software and underlying
filesystems. If a client makes a request such as `open`, `readdir`,
`stat`, etc. it must translate that into something that makes sense
across multiple filesystems. For `readdir` that means running the same
call against all branches and aggregating the output. For `open` that
means finding the file to open and doing so. The only way to find the
file to open is to scan across all branches and sort the results and
pick one. There is some variability in how much searching is done due
to
[policies](../config/functions_categories_policies.md#policy-descriptions)
but not much. There is no practical way to do otherwise. Especially
given so many mergerfs users expect out-of-band changes to "just
work."
The best way to limit spinup of drives is to limit their usage at the
client level. Meaning keeping software from interacting with the
@@ -33,21 +42,24 @@ filesystems and on startup everything scanned and stored. From then on
it would have to carefully update all the same data the filesystems
do. It couldn't be kept in RAM because it would take up too much space
so it'd have to be on a SSD or other storage device. If anything
changed out of band it would break things in weird ways. It could
rescan on occasion but that would require spinning up
everything. Filesystem watches could be used to get updates when the
filesystem changes but that would allow for race conditions and
might keep the drives from spinning down. Something as "simple" as
keeping the current available free space on each filesystem isn't as
easy as one might think given reflinks, snapshots, and other block
level dedup technologies as well as the space used includes not just
raw file usage.
changed out of band it would break things in weird ways (and MANY
users depend on out of band changes working.) It could rescan on
occasion but that would require spinning up everything. Filesystem
watches could be used but since mergerfs is just another piece of
software interacting with the filesystem ALL mergerfs requests would
be echoed back to it causing lots of overhead throwing away a super
majority of events it triggered. And something as "simple" as keeping
the current available free space on each filesystem is not as easy as
one might think given reflinks, snapshots, and other block level dedup
technologies distort the meaning of usage values.
Even if all metadata (including xattrs) is cached some software will
open files (media like videos and audio) to check their
metadata. Granted a Plex or Jellyfin scan which may do that is
different from a random directory listing but is still something to
consider. Those "deep" scans can't be kept from waking drives.
Only if ALL metadata and all file locations were cached could mergerfs
then do very targeted file operations so as not to trigger a scan
across the pool. But that will break out of band interactions. It
would require accepting possibly bad behavior by space based policies
since space would be imprecisely computed. It would require long scans
of the filesystems on startup and a separate location to store all
this data.
### What if you only query already active drives?
@@ -56,7 +68,8 @@ Let's assume that is plausible (it isn't because some drives actually
will spin up if you ask if they are spun down... yes... really) you
would have to either cache all the metadata on the filesystem or treat
it like the filesystem doesn't exist. The former has all the problems
mentioned prior and the latter would break a lot of things.
mentioned prior and the latter would break a lot of things. Suddenly
whole filesystems worth of data would be missing because it spun down.
### Is there anything that can be done where mergerfs is involved?
@@ -65,15 +78,77 @@ Yes, but whether it works for you depends on your tolerance for the
complexity.
1. Cleanly separate writing, storing, and consuming the data.
1. Use a SSD or dedicated and limited pool of drives for downloads / torrents.
2. When downloaded move the files to the primary storage pool.
3. When setting up software like Plex, Jellyfin, etc. point to the
1. Use a SSD or dedicated and limited pool of drives for downloads / torrents.
2. When downloaded move the files to the primary storage pool.
3. When setting up software like Plex, Jellyfin, etc. point to the
underlying filesystems. Not mergerfs.
2. Add a bunch of bcache, lvmcache, dm-cache, or similar block level
cache to your setup. After a bit of use, assuming sufficient
storage space, you can limit the likelihood of the underlying
spinning disks from needing to be hit.
3. Tweak your system's settings around caching:
* mount all your filesystems with `noatime` to disable updating
"last accessed" time on files.
* mount all your filesystems with `nodiratime` to disable updating
"last accessed" time on directories.
* `sudo sysctl -w vm.vfs_cache_pressure=10`: Prioritizes keeping
file metadata cached in RAM.
* `sudo sysctl -w vm.dirty_ratio=40`: Allows up to 40% of system
memory to be filled with unwritten "dirty" data before forcing a
disk flush.
* `sudo sysctl -w vm.dirty_background_ratio=5`: Sets a lower
threshold (5%) for the kernel to start writing data in the
background without blocking applications.
* `sudo sysctl -w vm.dirty_writeback_centisecs=6000`: Increases
the interval between kernel writeback checks to 60 seconds
(default is usually 5).
* `sudo sysctl -w vm.dirty_expire_centisecs=12000`: Allows data to
stay "dirty" in RAM for up to 120 seconds before it must be
written to disk.
* To make `sysctl` values permanent add the above to
`/etc/sysctl.conf`.
* **Note:** for systems with limited RAM the above `sysctl` values
could cause issues.
* **Note:** if you increase the time for which data is stored in
RAM before being flushed to disk there is also an increased risk
that a sudden powerloss could lead to lost data.
4. Enable all the appropriate caches in mergerfs:
* [https://trapexit.github.io/mergerfs/latest/config/cache/](https://trapexit.github.io/mergerfs/latest/config/cache/)
* `cache.entry=86400`: cache file existence for 24h.
* `cache.negative-entry=86400`: cache file non-existence for 24h.
* `cache.attr=86400`: cache file metadata for 24h.
* `cache.symlinks=true`: enable symlink caching.
* `cache.readdir=true`: enable directory listing caching.
Remember too that while it may be a tradeoff you are willing to live
with there is decent evidence that spinning down drives puts increased
wear on them and can lead to their death earlier than otherwise.
### References
* [Documentation for /proc/sys/vm/](https://docs.kernel.org/admin-guide/sysctl/vm.html)
* [How to Configure vm.swappiness and Other Virtual Memory Parameters on RHEL](https://oneuptime.com/blog/post/2026-03-04-configure-vm-swappiness-virtual-memory-rhel-9/view)
### Tradeoffs
While it may be a trade off you are willing to live with there is
decent evidence that spinning down drives puts material increased wear
on them and can lead to their death earlier than otherwise. You may
end up saving money on electricity but spending more on having to
purchase new drives sooner.
## Alternatives
[Project Comparisons](../project_comparisons.md) lists a few projects
that are similar to mergerfs but offer caching. Please note that these
projects are very young and not general purpose like mergerfs. Their
compliance to POSIX filesystem rules is probably low enough that
certain behaviors may not work or worse lead to data corruption.
I'm not suggesting not to look at those projects but filesystem
development is complicated even when it appears to be just a layer on
top of another. Just be careful and do your research. That stands for
anything touching your data... mergerfs included.
To the degree that mergerfs can add features which help in this
situation, so long as it doesn't interfere with other fundamental
features, they may be added over time.
@@ -1,53 +0,0 @@
# Recommendations and Warnings
## What should mergerfs NOT be used for?
* databases: Even if the database stored data in separate files
(mergerfs wouldn't offer much otherwise) the higher latency of the
indirection will really harm performance. If it is a lightly used
sqlite3 database then it should be fine.
* VM images: For the same reasons as databases. VM images are accessed
very aggressively and mergerfs will introduce a lot of extra latency.
* As replacement for RAID: mergerfs is just for pooling branches. If
you need that kind of device performance aggregation or high
availability you should stick with RAID. However, it is fine to put
a filesystem which is on a RAID setup in mergerfs.
**However, if using [passthrough](../config/passthrough.md) the above
situations are less likely to be a concern. Best to do testing for
your specific use case.**
## It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
[mhddfs](https://github.com/trapexit/mhddfs) manages running as
`root` by calling
[getuid()](https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319)
and if it returns `0` then it will
[chown](http://linux.die.net/man/1/chown) the file. Not only is that a
race condition but it doesn'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
[setegid](http://linux.die.net/man/2/setegid), in effect, becoming the
user making the original call, and perform the action as them. 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 Samba, mergerfs uses
`syscall(SYS_setreuid,...)` to set the callers credentials for that
thread only. Jumping back to `root` as necessary should escalated
privileges be needed (for instance: to clone paths between
filesystems).
For non-Linux systems, mergerfs uses a read-write lock and changes
credentials only when necessary. If multiple threads are to be user X
then only the first one will need 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'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't starve. This isn't
the best solution but should work reasonably well assuming there are
few users.
@@ -1,26 +1,76 @@
# Technical Behavior and Limitations
## Do hardlinks work?
## Do hard links work?
Yes. There is no option to enable or disable links (or renames.) They
are fundamentally supported in compatible situations. That said the
inode of a file is not necessarily indicative of two file names
linking to the same underlying data. See also the option `inodecalc`
for how inode values are calculated.
Yes. [links](https://en.wikipedia.org/wiki/Hard_link) (otherwise known
as hard links) are fundamentally supported by mergerfs. There is no
option to enable or disable links. All comments elsewhere claiming they
do not work are related to the setup of the user's system.
What mergerfs does not do is fake hard links across branches. Read the
section [rename & link](../config/rename_and_link.md) for how it
works.
See the [Intro to Filesystems](../intro_to_filesystems.md) page for more
details on what a `link` is.
Remember that hardlinks will **NOT** work across devices. That
includes between the original filesystem and a mergerfs pool, between
two separate pools of the same underlying filesystems, or bind mounts
of paths within the mergerfs pool. The latter is common when using
Docker or Podman. Multiple volumes (bind mounts) to the same
underlying filesystem are considered different devices. There is no
way to link or rename between them. You should mount in the highest
directory in the mergerfs pool that includes all the paths you need if
you want links and rename to work.
A limitation of a `link` is that it **must** reference a file/inode
within the same filesystem or device. When a request is made to the
operating system to `link` (or `rename`) a file to a target location
the OS will first check if the source and target are the same
filesystem / device. If not it will return the error `EXDEV` meaning
that the request is not able to be made due to the result being "cross
device".
What catches many users off guard is that this "cross device" check is
not very deep or involved. Even the same filesystem bind mounted to
another location will typically be considered a different device. So
when mergerfs is creating a union of other filesystems there is no way
to link or rename from one of the underlying branches into
mergerfs. They are different filesystems even if logically they seem
related or the same. And similarly it is impossible for mergerfs to
truly make a link between two branches which are not in fact the same
mounted filesystem.
This is relevant to mergerfs users because bind mounts are very common
when using container runtimes such as Docker, Podman, or
Kubernetes. When you add a volume or mount to your container that is
in fact a bind mount of that path. Meaning if you were to add
`/mnt/mergerfs/downloads` and `/mnt/mergerfs/movies` as separate
mounts into your container any request to link between the two
locations within the container will fail with the `EXDEV` error even
though it will work perfectly fine on the host. If you wish for `link`
and `rename` to work you **MUST** mount into the container a point in
the directory structure above what you wish to use. In this case
`/mnt/mergerfs`.
Another situation where people will hit unexpected `EXDEV` errors is
when they use a policy which makes file creation decisions based on
existing paths. If the source of a `link` or `rename` is on branch A
but the target directory is on branch B, given the whole point of the
policy is to strictly enforce behavior based on what branches already
exist, it must return `EXDEV`. Doing otherwise would fundamentally
break the purpose of the policy. Read [rename &
link](../config/rename_and_link.md) for more details on how mergerfs
handles those two functions.
When in doubt it is trivial to test `link`:
```shell
$ cd /mnt/mergerfs/
$ touch foo
$ ln -v foo bar
'bar' => 'foo'
$ stat foo bar | grep Inode
Device: 0,173 Inode: 123456 Links: 2
Device: 0,173 Inode: 123456 Links: 2
```
For the more technical: within mergerfs the inode of a file is not
necessarily indicative of two file names linking to the same
underlying data. For more details as to why that is see the docs on
[inodecalc](../config/inodecalc.md).
## Are there any size limitations to files, directories, etc.?
Whatever the limits are of the underlying filesystems.
## Do reflink, FICLONE, or FICLONERANGE work?
@@ -101,7 +151,7 @@ You can remove the reserve by running: `tune2fs -m 0 <device>`
## I notice massive slowdowns of writes when enabling cache.files.
When file caching is enabled in any form (`cache.files!=off`) it will
When file caching is enabled in any form (`cache.files!=off`) it may
issue `getxattr` requests for `security.capability` prior to _every
single write_. This will usually result in performance degradation,
especially when using a network filesystem (such as NFS or SMB.)
@@ -133,20 +183,21 @@ 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
* Higher IO latency due to the trips in and out of kernel space
(though now minimized with passthrough IO)
* 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
- Much lower barrier to entry (getting code into the kernel takes a
* 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
* Much lower barrier to entry (getting code into the kernel takes a
lot of time and effort initially)
@@ -175,29 +226,81 @@ removed to simplify the codebase.
## How does mergerfs handle credentials?
mergerfs is a multithreaded application in order to handle requests
from the kernel concurrently. Each FUSE message has a header with
certain details about the request including the process ID (pid) of
the requesting application, the process' effective user id (uid), and
group id (gid). To ensure proper POSIX filesystem behavior and
security mergerfs must change its identity to match that of the
requester when performing the certain functions on the underlying
filesystem. As required by standards most Unix/POSIX based systems a
process and all its threads are under the same uid and gid. However,
on Linux each thread **may** have its own credentials. This allows
mergerfs to be multithreaded and for each thread to change to the
credentials as required by the incoming message it is
handling. However, currently on FreeBSD this is not possible (though
there has been
[discussions](https://wiki.freebsd.org/Per-Thread%20Credentials)) and
as such must change the credentials of the whole application when
actioning messages. mergerfs does optimize this behavior by only
changing credentials and locking the thread to do so if the process is
currently not the same as what is necessary by the incoming
request. As a result of this design FreeBSD may experience more
contention and therefore lower performance than Linux.
group id (gid).
Additionally, mergerfs [utilizes a cache for supplemental
groups](../known_issues_bugs.md#supplemental-user-groups) due the the
high cost of querying that information.
FUSE and the kernel have two ways of managing permissions. A kernel
side `default_permissions` option and leaving it to the FUSE
server. When default permissions is enabled the kernel will do the
entitlement checks and only allow requests through which should be
allowed according to normal POSIX permissions. When not enabled it is
the responsibility of the FUSE server, in this case mergerfs, to do
whatever is necessary to manage entitlements.
Prior to mergerfs v2.42.0 it would enable `default_permissions` but
also leveraged the uid and gid available in each FUSE request. The
thread actioning the request would change its credentials to match
those of the requesting application to ensure permissions were
properly handled as well as dealing with some quirks of non-POSIX
compatible filesystems. However, this strategy had two major
issues. First, it was not compatible with the new `allow-idmap`
feature of FUSE which allows a filesystem to advertise it can be used
with id mapping which is often used with containers. Secondly, it
caused permission issues when the kernel would say something was
allowed but due to the way mergerfs was changing creds would
fail. This mostly came in the form of `chroot`ed setups like used in
containers. Neither of these tended to impact casual users but did
break some niche or power user use cases. Since the casual user won't
notice the subtle changes and it would enable new use cases it was
decided that the credential handling would change.
As of v2.42.0 mergerfs now runs as root more generally. Either
changing credentials when creating files (Linux) or chown'ing them
after creation (FreeBSD).
As a result of this change it is now necessary for the FUSE
`default_permissions` feature be used for proper entitlements
management. mergerfs does allow it to be disabled but it should only
be done so for debugging purposes.
## Does mergerfs support idmap?
Yes, by setting [allow-idmap=true](../config/options.md) (which is the
default.)
Requires that
[kernel-permissions-check](../config/kernel-permissions-check.md) be
enabled (the default.)
## What happens if a branch filesystem blocks?
POSIX filesystem calls unfortunately are entirely synchronous. If the
underlying filesystem mergerfs is interacting with freezes up or
blocks then the thread issuing the request will block same as any
other piece of software. If enough threads block then mergerfs will
block. There are no timeouts or ways to truly work around this
situation. If mergerfs spawned new threads it is very likely they too
would end up blocked.
## What information does mergerfs have when making decisions?
Every incoming request contains:
* The `tid` or `pid` of the calling thread/process
* The `uid` and `gid` of the calling thread/process
* The `umask` of the calling thread/process
* The arguments of the filesystem call in question
Naturally the mergerfs config as well as anything queriable from the
operating system or filesystems are also available.
## Why does running `mount -a` result in new instances of mergerfs?
See [lazy-umount-mountpoint](../config/lazy-umount-mountpoint.md)
+56 -2
View File
@@ -1,5 +1,26 @@
# Usage and Functionality
## What should mergerfs NOT be used for?
* Situations where you need large amounts of contiguous space beyond
that available on any singular device. Such as putting 10GiB a file
on 2 6GiB filesystems.
* databases: Even if the database stored data in separate files
(mergerfs wouldn't offer much otherwise) the higher latency of the
indirection will harm performance. Though if it is a sqlite3
database then its likely fine.
* VM images: For the same reasons as databases. VM images are accessed
very aggressively and mergerfs will introduce a lot of extra latency.
* As replacement for RAID: mergerfs is just for pooling branches. If
you need device performance aggregation or high availability you
should stick with RAID. However, it is fine to put a filesystem
which is on a RAID setup in mergerfs.
**However, if using [passthrough](../config/passthrough.md) the
performance related issues above are less likely to be a concern. Best
to do testing for your specific use case.**
## What happens when file paths overlap?
It depends on the situation and the configuration of mergerfs. The
@@ -9,13 +30,19 @@ filesystem function is requested. Which file to select or act on.
See how [policies](../config/functions_categories_policies.md) work.
## Is adding or removing mergerfs to/from my setup destructive?
No. [See
below.](#can-mergerfs-be-used-with-filesystems-which-already-have-data)
## Can mergerfs be used with filesystems which already have data?
Yes. mergerfs is really just a proxy and does **NOT** interfere with
the normal form or function of the filesystems, mounts, or paths it
manages. It literally is interacting with your filesystems as any
other application does. It can not do anything that any other random
piece of software can't do.
piece of software can't do. There is no migration needed.
mergerfs is **not** a traditional filesystem that takes control over
the underlying drive, block device, or partition. mergerfs is **not**
@@ -23,6 +50,9 @@ RAID. It does **not** manipulate the data that passes through it. It
does **not** shard data across filesystems. It only shards some
**behavior** and aggregates others.
Think of it as a filesystem [reverse
proxy](https://en.wikipedia.org/wiki/Reverse_proxy).
## Can mergerfs be removed without affecting the data?
@@ -105,9 +135,33 @@ normal conditions.
Keep in mind that if out-of-band changes are made while also
leveraging any caching of the filesystem layout such as with
`cache.entry`, `cache.negative_entry`, `cache.attr`, `cache.symlinks`,
`cache.entry`, `cache.negative-entry`, `cache.attr`, `cache.symlinks`,
or `cache.readdir` you may experience temporary inconsistency till the
cache expires. mergerfs is not actively watching all branches for
changes and the kernel will have no way to know anything changed so as
to clear or ignore the cache. This is the same issue you can have with
[remote filesystems](../remote_filesystems.md).
## What are the performance characteristics of using mergerfs?
mergerfs does not stripe or shard data. Instead it acts like a reverse
proxy / multiplexer. As such it is not possible to have RAID like
performance improvements. However, its performance is not limited like
[block level concatenation / JBOD /
SPAN.](../project_comparisons.md#raid0-jbod-span-drive-concatenation-striping)
* Single file / single stream: Max throughput limited to speed of the
chosen underlying branch + FUSE overhead (usually 5–30% slower than
direct access; near native with [IO
passthrough](../config/passthrough.md)).
* Aggregate / multi-stream: Can approach sum of all branches' speeds
when many independent IO operations run in parallel (multiple files,
processes, or clients).
* Other factors: Latency-sensitive workloads suffer more (slow
branches or network drives hurt responsiveness); directory listing
and metadata ops slower due to combining branches.
Real-world performance varies heavily with workload, kernel, options,
and hardware. See [benchmarking](../benchmarking.md) and
[performance](../performance.md) section for testing tips.
+31 -15
View File
@@ -36,15 +36,15 @@ and [FAQ](configuration_and_policies.md) for policy recommendations.
## 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?
One or multiple of the below are likely causing branches
One or multiple of the below are likely causing branches to be
[filtered out](../config/functions_categories_policies.md#filtering).
* [minfreespace](../config/minfreespace.md) could be filtering out
branches
* if using an `ep*`
[policy](../config/functions_categories_policies.md) there may not
be an available branch
* due to [branch modes](../config/branches.md#branch-mode) being set
to to `NC` or `RO` branches will be excluded from consideration
* using an `ep*` [policy](../config/functions_categories_policies.md)
and there may not be an available branch
* [branch modes](../config/branches.md#branch-mode) set to `NC` or
`RO` branches will be excluded from consideration
* if using `ext4` ensure you are not running into reserve limits (use
`tune2fs -m 0` to remove the reserve)
* it is possible that the filesystem selected by the policy has run
@@ -56,7 +56,7 @@ within the pool (behavior modified by [statfs](../config/statfs.md)
and [statfs_ignore](../config/statfs.md#statfs_ignore)). 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. Not the practical usable space.
theoretical space available but not the practical usable space.
## Why isn't the create policy working?
@@ -134,7 +134,7 @@ the use of mergerfs from [container platforms such as
Docker.](compatibility_and_integration.md)
## Why are file moves, renames, links/hardlinks failing?
## Why are file moves, renames, links/hard links failing?
mergerfs fully supports rename and link functions to the degree that
is possible in a union filesystem. Problems related to rename and link
@@ -225,18 +225,34 @@ the software's author and request proper handling of `EXDEV` errors.
### Additional Reading
* [rename and link](../config/rename_and_link.md)
* [Do hardlinks work?](technical_behavior_and_limitations.md#do-hardlinks-work)
* [Do hard links work?](technical_behavior_and_limitations.md#do-hard-links-work)
* [How does mergerfs handle moving and copying of files?](technical_behavior_and_limitations.md#how-does-mergerfs-handle-moving-and-copying-of-files)
* [Moving files and directories fails with Samba](../known_issues_bugs.md#moving-files-and-directories-fails-with-samba)
## Why is rtorrent failing with ENODEV (No such device)?
## Why is software needing mmap failing?
Be sure to set
[cache.files=partial|full|auto-full|per-process](../config/cache.md)
or use Linux kernel v6.6 or above. 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.
See [Known Issues and Bugs#3rd Party
Software](../known_issues_bugs.md#3rd-party-software) and
[QuickStart](../quickstart.md).
## Why don't quick scans fail to pick up new media?
Media consumption software like Plex, Jellyfin, Emby, Airsonic,
Navidrome, etc. will use timestamps of directories to see if it should
be scanned. Be sure to set `func.getattr=newest` to catch that.
A full scan would still pick up all media and for those concerned
about device activity it is recommened to disable quick scans and
leave periodic full scans so the activity is managed. Or to configure
your software which manages downloads (such as *Arr family of
software) to trigger specific scans when files are added to the pool.
Additional reading:
* [Does inotify and fanotify
work?](compatibility_and_integration.md#does-inotify-and-fanotify-work)
## Why don't I see mergerfs options in mount command or /proc/mounts?
+77 -18
View File
@@ -1,30 +1,65 @@
# mergerfs - a featureful union filesystem
**mergerfs** is a
[FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace) based
[union filesystem](https://en.wikipedia.org/wiki/Union_mount) geared
towards simplifying storage and management of files across numerous
commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
**aufs**, **DrivePool**, etc.](project_comparisons.md)
**mergerfs** is a [union
filesystem](https://en.wikipedia.org/wiki/Union_mount) that makes
multiple filesystems and/or directories appear as a single unified
directory. mergerfs is designed to simplify how you manage files
across multiple independent filesystems without the complexity,
fragility, or cost of RAID or similar storage aggregation
technologies.
mergerfs as a smart pooling layer allows you to combine any number of
[existing
filesystems](faq/usage_and_functionality.md#can-mergerfs-be-used-with-filesystems-which-already-have-data);
whether they are on hard drives, SSDs, network shares, or other
mounted storage; into what looks like one large filesystem, while
still maintaining direct access to each individual filesystem. Unlike
RAID, there's no rebuild process if a device fails. You only lose
those files that were on that specific filesystem. You can also add or
remove filesystems at any time without restructuring the pool.
mergerfs excels at cost-effective storage expansion, making it ideal
for media libraries, backups, archival data, and other write-sometimes,
read-often workloads where you need lots of space but don't want
the overhead of traditional storage aggregation technologies.
**Key advantages:**
* [Non-destructive](faq/usage_and_functionality.md#is-adding-or-removing-mergerfs-tofrom-my-setup-destructive) adding and removing of filesystems (even at runtime)
* Mix and match filesystems of any [size](faq/technical_behavior_and_limitations.md#are-there-any-size-limitations-to-files-directories-etc),
[type](faq/compatibility_and_integration.md#what-filesystems-can-be-used-as-branches),
or [underlying
device](faq/compatibility_and_integration.md#what-types-of-storage-devices-does-mergerfs-work-with)
* No parity calculations or rebuild times
* Does not require hard drives to be spinning if not in use
* [Direct access to files](faq/usage_and_functionality.md#can-filesystems-still-be-used-directly-outside-of-mergerfs-while-pooled) on individual filesystems when needed
* Flexible [policies](config/functions_categories_policies.md) for controlling where new files are created
For users seeking alternatives to mhddfs, unionfs, aufs, or DrivePool,
mergerfs offers a mature, actively maintained solution with extensive
configuration options and documentation. See the [project comparisons
for more details.](project_comparisons.md)
## Features
* Logically combine numerous filesystems/paths into a single
mount point (JBOFS: Just a Bunch of FileSystems)
mount point (JBOFS: Just a Bunch of FileSystems, think filesystem
[reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy))
* Combine paths of the same or different filesystems
* Ability to add or remove filesystems/paths without impacting the
rest of the data
* Unaffected by individual filesystem failure
* Configurable file selection and creation placement
* Configurable file creation placement
* File IO [passthrough](config/passthrough.md) for near native IO
performance (where supported)
* Works with filesystems of any size
* Works with filesystems of [almost any
type](faq/compatibility_and_integration.md#what-filesystems-can-be-used-as-branches)
* Ignore read-only filesystems when creating files
* Hard link [copy-on-write / CoW](config/link_cow.md)
* [Runtime configurable](runtime_interface.md)
* [Hard links](faq/technical_behavior_and_limitations.md#do-hard-links-work)
* Hard link [copy-on-write / CoW](config/link-cow.md)
* [Runtime configurability](runtime_interface.md)
* Support for extended attributes (xattrs)
* Support for file attributes (chattr)
* Support for POSIX ACLs
@@ -32,18 +67,23 @@ commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
## Non-features
* Read/write overlay on top of read-only filesystem like OverlayFS
* RAID like redundancy (see [SnapRAID](https://www.snapraid.it) and
[NonRAID](https://github.com/qvr/nonraid))
* RAID like performance improvements
* LVM/RAID style block device aggregation
* Data integrity checks, snapshots, file versioning
* Read/write overlays on top of read-only filesystems (like OverlayFS)
* File whiteout
* RAID like parity calculation (see [SnapRAID](https://www.snapraid.it))
* Redundancy
* Splitting of files across branches
* Active rebalancing of content
* [Actively attempt to keep hard drives spun down](faq/limit_drive_spinup.md)
## How it works
mergerfs logically merges multiple filesystem paths together. Not
block devices, not filesystem mounts, just paths. It acts as a proxy
to the underlying filesystem paths. Combining the behaviors of some
to the underlying filesystem. Combining the behaviors of some
functions and being a selector for others.
When the contents of a directory are requested mergerfs combines the
@@ -56,8 +96,9 @@ which branch will be selected for the creation.
For functions which change attributes or remove the file the behavior
may be applied to all instances found.
Read more about [policies
here](config/functions_categories_policies.md).
The way in which mergerfs behaves is controlled by the
[config / options / settings](config/options.md). More specifically by
[policies](config/functions_categories_policies.md).
### Visualization
@@ -74,7 +115,7 @@ A + B = C
| | |
+-- /dir2 | +-- /dir2
| | | | |
| *-- file4 | | +-- file4
| +-- file4 | | +-- file4
| | |
| +-- /dir3 +-- /dir3
| | | | |
@@ -84,7 +125,25 @@ A + B = C
+-- file7 +-- file7 +-- file7
```
## Getting Started
Head to the [quick start guide](quickstart.md).
## About This Documentation
* Like the software the documentation changes over time. Ensure that
you are reading the documentation related to the version of the
software you are using.
* The documentation is explicit, literal, and reasonably thorough. If
a suspected feature is not mentioned it does not exist. Do not read
into the wording. What is described is how it functions. If you feel
like something is not explained sufficiently or missing please [ask
in one of the supported forums](support.md#contact-issue-submission)
and the docs will be updated.
* The search feature of MkDocs is not great. Searching for "literal
strings" will generally not work. Alternative solutions are being
investigated.
* While not intended for end users nor completely accurate some might
find the AI generated docs at https://deepwiki.com/trapexit/mergerfs
interesting or useful.
+94 -45
View File
@@ -4,44 +4,53 @@
### FreeBSD version
* FreeBSD doesn't have per thread credentials meaning threads must
block to change credentials as required by numerous filesystem
functions. This impacts performance.
* FreeBSD's FUSE implementation is lacking many features of Linux.
* passthrough
* [https://wiki.freebsd.org/FUSEFS](https://wiki.freebsd.org/FUSEFS)
* FreeBSD does not have per thread credentials nor Linux capabilities
like abilities meaning it runs as root and has to create files as
root then chown them rather than changing credentials and creating
the file as the uid:gid.
* FreeBSD does not have `getdents` like APIs for reading
directories. As a result it uses traditional `readdir`.
* FreeBSD's FUSE implementation is lacking many features of the Linux
version.
* runtime interface
* ioctl support
* IO passthrough
* statx
* lazy umount
* oom_score_adj
* fuse_msg_size
* fuse-msg-size
* kernel symlink caching
* kernel readdir caching
* writeback caching
* idmap
* ...
### Supplemental user groups
#### Supplemental group caching
Due to the overhead of
[getgroups/setgroups](http://linux.die.net/man/2/setgroups) mergerfs
utilizes a cache. As necessary the supplemental group information will
be queried and cached. That cached list of groups will be used to set
the supplement groups as necessary. Due to the high cost of querying
the group list the default expiry for said data is 1 hour and after 12
hours of no usage will be removed from the cache all together.
#### Host vs Container identity
While not a bug some users have found when using containers that
While **not** a bug some users have found when using containers that
supplemental groups defined inside the container don't work as
expected. Since mergerfs lives outside the container it is querying
the host's group database. Containers have their own user and group
definitions unless setup otherwise just as different systems would.
expected. Since mergerfs (typically) lives outside the container it is
querying the host's supplemental group database. Containers have their
own user and group definitions unless setup otherwise just as
different systems would.
Users should mount in the host group file into the containers or use a
standard shared user and groups technology like NIS or LDAP.
If using Docker or Podman containers with mergerfs running outside
then you can pass in `/etc/passwd` and `/etc/group`.
```
docker run \
-v /etc/passwd:/etc/passwd:ro \
-v /etc/group:/etc/group:ro \
-u $(id -u):$(id -g) \
-v /data:/data \
containerimage
```
### directory mtime is not being updated
@@ -91,36 +100,76 @@ on [page caching](config/cache.md).
## 3rd Party Software
### SQLite3, Plex, Jellyfin do not work with mergerfs
### Software using mmap
It does. If you're trying to put the software's config / metadata /
database on mergerfs you can't set [cache.files=off](config/cache.md)
(unless you use Linux v6.6 or above and
[direct-io-allow-mmap](config/options.md) is enabled) because they are
using **sqlite3** with **mmap** enabled and have failed to properly
handle the situation where **mmap** may not be available.
* software using sqlite3 w/ mmap
* Plex (for config, not media)
* Jellyfin (for config, not media)
* Emby (for config, not media)
* Radarr
* Sonarr
* Lidarr
* other
* rtorrent
* qbittorrent (with 'Disk IO type = Memory mapped files')
[mmap](https://en.wikipedia.org/wiki/Mmap) is a filesystem and
operating system feature allowing files to be interacted with as if it
was normal memory. Support for `mmap` is **required** by a number of
projects. FUSE (and therefore mergerfs), in order to support `mmap`,
requires [page caching](config/cache.md) to be enabled. IE
`cache.files` must not be set to `off` (if using Linux before v6.6 or
mergerfs before v2.41.0.) In newer versions of mergerfs and Linux
`off` will work as it can now auto enable page caching (and therefore
`mmap`) as needed.
When `mmap` is needed but not available you may see `ENODEV` or `No
such device` errors though it will depend on the particular software.
Details on enabling `mmap` can be found at:
* [QuickStart](quickstart.md#configuration)
* [Config > cache.files](config/cache.md#cachefiles)
That said it is recommended that config and runtime files be stored on
SSDs on a regular filesystem for performance reasons. See [What should
mergerfs NOT be used
for?](faq/recommendations_and_warnings.md#what-should-mergerfs-not-be-used-for).
for?](faq/usage_and_functionality.md#what-should-mergerfs-not-be-used-for). Though
with [passthrough.io](config/passthrough.md) enabled that is less
of a concern.
Other software that leverages **sqlite3** which require **mmap**
includes Radarr, Sonarr, and Lidarr. However, many programs use
**sqlite3** and do not require **mmap**.
Technically **sqlite3** does not require `mmap`, and in fact the
default is not to use it, but many programs enable the feature and
provide no error handling when not supported. Ideally the software
would catch the error and use traditional IO instead. Not only is it
more compatible but could also be more performant in certain
situations.
It is recommended that you reach out to the developers of the software
you are having troubles with and ask them to add a fallback to regular
file IO when **mmap** is unavailable. It is not only more compatible
but also can be more performant in certain situations.
If the issue is that quick scans do not seem to pick up media then be
sure to set `func.getattr=newest`. That said a full scan will pick up
all media and it will put less load on the host to use time based
library scans or to configure downloading software to trigger a scan
when files are added to the pool. See [Does inotify and fanotify
work?](faq/compatibility_and_integration.md#does-inotify-and-fanotify-work)
for more details.
### qbittorrent
Same `mmap` concerns as mentioned above but called out specifically
due to regularity of queries.
The default "Disk IO type" setting is "Memory mapped files" which
means it requires `mmap` support.
If qbittorrent errors when using mergerfs ensure that either "Disk IO
type" is set to "POSIX-compliant" or "Simple pread/pwrite"; or setup
mergerfs to support `mmap`.
References:
* [Software using mmap](#software-using-mmap)
* [QuickStart](quickstart.md#configuration)
* [Config > cache.files](config/cache.md#cachefiles)
### rsync
This isn't specific to mergerfs but when using `--sparse, -S` option
with rsync the block size used to read and write data changes from
256KiB to 1KiB. This significantly impacts performance.
### backup software
@@ -134,7 +183,7 @@ may need to disable the use of the inode in the software if possible
or change the [inodecalc](config/inodecalc.md) setting if possible.
Generally speaking, in modern setups where there are many non-fully
complient POSIX filesystem in use, reliance on inode to have a
compliant POSIX filesystems in use, reliance on inode to have a
specific meaning is not always reasonable. FUSE, for instance, has no
requirement that inode values need to be unique or that two files that
happen to be the same underlying file have the same inode.
+5 -1
View File
@@ -29,6 +29,7 @@
* 2024-02-07 - [Designing & Deploying MANS - A Hybrid NAS Approach with SnapRAID, MergerFS, and OpenZFS](https://blog.muffn.io/posts/part-3-mini-100tb-nas)
* 2024-03-11 - [Using MergerFS to combine multiple hard drives into one unified media storage](https://fullmetalbrackets.com/blog/two-drives-mergerfs/)
* 2024-12-20 - [Pooling multiple drives on my Raspberry Pi with mergerfs](https://sebi.io/posts/2024-12-20-pooling-multiple-drives-with-mergerfs/)
* 2026-01-03 - [NAS-2026 | MergerFS and SnapRAID](https://github.com/DoTheEvo/NAS-MergerFS-SnapRAID)
## Videos
@@ -62,9 +63,12 @@
* 2024-01-05 - [OpenMediaVault MergerFS Tutorial (Portuguese)](https://www.youtube.com/watch?v=V6Yw86dRUPQ)
* 2024-02-19 - [Setup and Install MergerFS and SnapRAID (Part 1)](https://noted.lol/mergerfs-and-snapraid-setup-1/)
* 2024-02-22 - [Setup and Install MergerFS and SnapRAID (Part 2)](https://noted.lol/mergerfs-and-snapraid-setup-part-2/)
* 2024-05-29 - [Building a 26TB Offsite Backup Server! ft. Tailscale](https://www.youtube.com/watch?v=W_l82GF00UY)
* 2024-11-15 - [Meu servidor NAS - Parte 18: Recuperando um HD, recuperando o MergerFS e os próximos passos do NAS!](https://www.youtube.com/watch?v=5fy98kPzE3s)
* 2025-04-23 - [How to build the Perfect Media Server | Part 1 - The Tech Stack | mergerfs, SnapRAID, and docker.](https://www.youtube.com/watch?v=Yt67zz9p0FU)
* 2025-06-12 - [DIY Unraid-like functionality ](https://www.youtube.com/watch?v=fR0XhaWvR1E)
* 2025-06-12 - [DIY Unraid-like functionality](https://www.youtube.com/watch?v=fR0XhaWvR1E)
* 2025-09-22 - [استفاده از Snapraid و MergerFS | جایگزین RAID5](https://www.youtube.com/watch?v=GA6OvaUBKXs)
* 2026-02-10 - [Never run out of space again! Using MergerFS with Immich to expand your storage seamlessly](https://www.youtube.com/watch?v=Vbu4WzWUikQ)
## Podcasts
+14 -13
View File
@@ -1,15 +1,17 @@
# Tweaking Performance
`mergerfs` is effectively a filesystem proxy and therefore its
**mergerfs** is effectively a filesystem proxy and therefore its
theoretical max performance is that of the underlying devices
(ignoring caching performed by the kernel.) However, given it is a
FUSE based 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's
FUSE based filesystem working from userspace and it must combine the
behavior and information from multiple underlying branches there can
be an increase in overhead relative to other solutions. That said the
performance of certain functions, such as IO, can match the
theoretical max but it depends greatly on the system's
configuration. There are many things which can impact
performance. Device speeds and latency, network speeds and latency,
concurrency and parallel limits of the hardware, read/write sizes,
etc.
concurrency and parallel limits of the hardware, read/write sizes, the
number of branches, etc.
While some settings can impact performance they are all **functional**
in nature. Meaning they change mergerfs' behavior in some way. As a
@@ -24,23 +26,22 @@ before changing them to understand how functionality will change.
* test theoretical performance using `nullrw` or using a ram disk as a
branch
* enable [passthrough](config/passthrough.md) (likely to have the
* enable [passthrough.io](config/passthrough.md) (likely to have the
biggest impact)
* change read or process [thread pools](config/threads.md)
* toggle [func.readdir](config/func_readdir.md)
* change [func.readdir](config/func_readdir.md)
* increase [readahead](config/readahead.md): `readahead=1024`
* disable `security_capability` and/or [xattr](config/xattr.md)
* disable `security-capability` and/or [xattr](config/xattr.md)
* increase cache timeouts [cache.attr](config/cache.md#cacheattr),
[cache.entry](config/cache.md#cacheentry),
[cache.negative_entry](config/cache.md#cachenegative_entry)
[cache.negative-entry](config/cache.md#cachenegative-entry)
* toggle [page caching](config/cache.md#cachefiles)
* enable `parallel-direct-writes`
* enable [cache.writeback](config/cache.md#cachewriteback)
* enable [cache.statfs](config/cache.md#cachestatfs)
* enable [cache.symlinks](config/cache.md#cachesymlinks)
* enable [cache.readdir](config/cache.md#cachereaddir)
* disable `posix_acl`
* disable `async_read`
* disable `posix-acl`
* disable `async-read`
* use [symlinkify](config/symlinkify.md) if your data is largely
static and read-only
* use [tiered cache](extended_usage_patterns.md) devices
+455 -152
View File
@@ -1,20 +1,32 @@
# Project Comparisons
There are no solutions, only trade-offs.
## mhddfs
* [https://romanrm.net/mhddfs](https://romanrm.net/mhddfs)
mhddfs is, like mergerfs, a FUSE-based filesystem which can pool
multiple filesystems together.
mhddfs had not been updated in over a decade and has known stability
and security issues. mergerfs provides a super set of mhddfs' features
and offers better performance. In fact, as of 2020, the author of
mhddfs has [moved to using
mergerfs.](https://romanrm.net/mhddfs#update)
and security issues. mergerfs provides a superset of mhddfs' features
and offers better performance. As of 2020, the author of mhddfs has
[moved to using mergerfs.](https://romanrm.net/mhddfs#update)
* **Better than mergerfs for:** legacy setups that already depend on
mhddfs-specific behavior and do not need newer features.
* **Worse than mergerfs for:** most modern deployments due to maintenance
status, feature coverage, and reliability.
Below is an example of mhddfs and mergerfs setup to work similarly.
`mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool`
```
mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool
`mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool`
mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool
```
## aufs
@@ -22,57 +34,91 @@ Below is an example of mhddfs and mergerfs setup to work similarly.
* [https://aufs.sourceforge.net](https://aufs.sourceforge.net)
* [https://en.wikipedia.org/wiki/Aufs](https://en.wikipedia.org/wiki/Aufs)
While aufs still is maintained it failed to be included in the
mainline kernel and is no longer available in most Linux distros
making it harder to get installed for the average user.
aufs, another union filesystem, is a kernel-based overlay filesystem
with basic file creation placement policies.
Compared with mergerfs, aufs focuses on kernel-level overlay and
copy-on-write behavior, while mergerfs focuses on userspace pooling of
existing filesystems with richer branch placement policies.
aufs failed to be included in the mainline kernel and is no longer
available in most Linux distros, making it harder to get installed for
the average user. Development has been largely dormant for years.
While aufs can often offer better peak performance due to being
primarily kernel based, mergerfs provides more configurability and is
generally easier to use. mergerfs however does not offer the overlay /
primarily kernel-based (at least when `passthrough.io` is disabled in
mergerfs), mergerfs provides more configurability and is generally
easier to use. mergerfs, however, does not offer the overlay /
copy-on-write (CoW) features which aufs has.
* **Better than mergerfs for:** kernel-level overlay/CoW workloads where
that behavior is required.
* **Worse than mergerfs for:** users needing a widely packaged,
actively-maintained, easy-to-deploy pooling solution.
## unionfs
* [https://unionfs.filesystems.org](https://unionfs.filesystems.org)
unionfs for Linux is a "stackable unification file system" which
functions like many other union filesystems. unionfs has not been
maintained and was last released for Linux v3.14 back in 2014.
functions like many other union filesystems. It was last released for
Linux v3.14 back in 2014.
Compared with mergerfs, unionfs is an older kernel union filesystem
implementation while mergerfs is a modern, actively-maintained
FUSE-based pooling filesystem with broader policy controls.
Documentation is sparse so a comparison of features is not possible
but given the lack of maintenance and support for modern kernels there
is little reason to consider it as a solution.
* **Better than mergerfs for:** historical or legacy environments that
already depend on this specific kernel unionfs behavior.
* **Worse than mergerfs for:** practical modern deployments due to
limited maintenance, aging kernel support, and sparse documentation.
## unionfs-fuse
* [https://github.com/rpodgorny/unionfs-fuse](https://github.com/rpodgorny/unionfs-fuse)
unionfs-fuse is more like unionfs, aufs, and overlayfs than mergerfs
in that it offers overlay / copy-on-write (CoW) features. If you're
just looking to create a union of filesystems and want flexibility in
file/directory placement then mergerfs offers that whereas
unionfs-fuse is more for overlaying read/write filesystems over
read-only ones.
unionfs-fuse is a FUSE-based union filesystem focused on overlay
behavior, including copy-on-write (CoW) style layering of writable
content over read-only content.
Since unionfs-fuse, as the name suggests, is a FUSE based technology
Compared with mergerfs, unionfs-fuse's primary feature is
overlay/CoW-style layering, while mergerfs' primary feature is
policy-based pooling and placement across multiple regular writable
filesystems.
Since unionfs-fuse, as the name suggests, is a FUSE-based technology
it can be used without elevated privileges that kernel solutions such
as unionfs, aufs, and overlayfs require.
* **Better than mergerfs for:** overlay-style unions with copy-on-write
semantics in userspace.
* **Worse than mergerfs for:** flexible create-policy-based pooling of
many writable filesystems.
## overlayfs
* [https://docs.kernel.org/filesystems/overlayfs.html](https://docs.kernel.org/filesystems/overlayfs.html)
overlayfs is effectively the successor to unionfs, unionfs-fuse, and
aufs and is widely used by Linux container platforms such as Docker and
Podman. It was developed and is maintained by the same developer who
created FUSE.
overlayfs is effectively the functional successor to unionfs,
unionfs-fuse, and aufs and is widely used by Linux container platforms
such as Docker and Podman. Both overlayfs and FUSE were originally
created by Miklos Szeredi.
If your use case is layering a writable filesystem on top of read-only
filesystems then you should look first to overlayfs. Its feature set
however is very different from mergerfs and solve different problems.
however is very different from mergerfs and solves different problems.
* **Better than mergerfs for:** container and image-layering workflows
where copy-on-write semantics are central.
* **Worse than mergerfs for:** heterogeneous pooling use cases that need
flexible branch selection policies across many existing filesystems.
## RAID0, JBOD, SPAN, drive concatenation, striping
@@ -82,32 +128,27 @@ however is very different from mergerfs and solve different problems.
* [SPAN](https://en.wikipedia.org/wiki/Non-RAID_drive_architectures#Concatenation_(SPAN,_BIG))
* [striping](https://en.wikipedia.org/wiki/Data_striping)
These are block device technologies which in some form aggregate
devices into what appears to be a singular device on which a
traditional filesystem can be used. The filesystem has no
understanding of the underlying block layout and should one of those
underlying devices fail or be removed the filesystem will be missing
that chunk which could contain critical information and the whole
filesystem may become unrecoverable. Even if the data from the
filesystem is recoverable it will take using specialized tooling to do
so.
These are block device technologies which, in some form, aggregate
devices into what appears to be a single device on which a traditional
filesystem can be used. The filesystem has no understanding of the
underlying block layout and, should one of those underlying devices
fail or be removed, the filesystem will be missing that chunk, which
could contain critical information, and the whole filesystem may
become unrecoverable. Even if the data from the filesystem is
recoverable, doing so may require specialized tooling.
In contrast, with mergerfs you can format devices as one normally
would or take existing filesystems and then combine them in a pool to
In contrast, with mergerfs you can format devices as you normally
would, or take existing filesystems and combine them in a pool to
aggregate their storage. The failure of any one device will have no
impact on the other devices. The downside to mergerfs' technique is
the fact you don't actually have contiguous space as large as if you
used those other technologies. Meaning you can't create a file greater
than 1TB on a pool of 2 1TB filesystems.
impact on the other devices. The downside to mergerfs' approach is
that you do not actually have contiguous space as large as you would
with those other technologies. That means you cannot create a file
larger than 1TB on a pool of two 1TB filesystems.
## BTRFS Single Data Profile
[BTRFS'](https://btrfs.readthedocs.io) `single` data profile is
similar to RAID0 but BTRFS can more explicitly place data and metadata
on multiple devices. However, like RAID0 and similar technologies if a
single device fails you may lose all data in the pool. BTRFS does have
some abilities to recover data but this is not guaranteed.
* **Better than mergerfs for:** creating one contiguous block device and
maximizing single-file size/performance characteristics.
* **Worse than mergerfs for:** failure isolation and straightforward
per-disk recovery when a device dies.
## RAID5, RAID6
@@ -115,30 +156,115 @@ some abilities to recover data but this is not guaranteed.
* [RAID5](https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_5)
* [RAID6](https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_6)
RAID5, RAID6, and similar technologies aggregate multiple devices at
the block level and provide one or more live parity schemes that can
allow the pool to continue to run should one device fail and rebuild
the data once the device is replaced.
mergerfs offers no parity or redundancy features so in that regard the
technologies are not comparable. [SnapRAID](https://www.snapraid.it)
can be used in combination with mergerfs to provide redundancy. Unlike
traditional RAID5 or RAID6 SnapRAID works with drives of different
sizes and can have more than 2 parity drives. However, parity
calculations are not done in real-time.
or [NonRAID](https://github.com/qvr/nonraid) can be used in
combination with mergerfs to provide redundancy. Unlike traditional
RAID5 or RAID6 SnapRAID works with drives of different sizes and can
have more than 2 parity drives. Parity calculations with SnapRAID are
not done in real-time but NonRAID's are.
See [https://www.snapraid.it/compare](https://www.snapraid.it/compare)
for more details and comparisons.
For more details and comparison of SnapRAID to related technologies
see [https://www.snapraid.it/compare](https://www.snapraid.it/compare).
* **Better than mergerfs for:** live parity protection and continued array
operation through configured disk failures.
* **Worse than mergerfs for:** incremental mixed-size pooling with simple
per-filesystem independence.
## LVM
* [https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)](https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux))
* [https://sourceware.org/lvm2](https://sourceware.org/lvm2)
LVM is a block-level volume management system for Linux. It combines
physical devices into volume groups and creates logical volumes on top,
with features like snapshots, resizing, and thin provisioning.
Compared with mergerfs, LVM operates below the filesystem while mergerfs
operates above filesystems. With LVM, filesystems are created on logical
volumes and do not know about the underlying disks. If a linear or
striped logical volume spans multiple disks and one disk fails, the
filesystem can become unreadable. With mergerfs, each disk typically has
its own independent filesystem, so a single disk failure affects only the
data on that disk.
* **Better than mergerfs for:** block-level features like thin
provisioning, snapshots, and online resizing.
* **Worse than mergerfs for:** incremental mixed-disk pooling where
per-disk failure isolation and per-disk native access are priorities.
## mdadm
* [https://en.wikipedia.org/wiki/Mdadm](https://en.wikipedia.org/wiki/Mdadm)
* [https://raid.wiki.kernel.org/index.php/A_guide_to_mdadm](https://raid.wiki.kernel.org/index.php/A_guide_to_mdadm)
`mdadm` is Linux userspace tooling for configuring and managing software
RAID arrays provided by the kernel `md` driver. It can build RAID0/1/4/5/6/10
and related layouts, monitor arrays, and handle rebuild workflows.
Like RAID technologies discussed above, `mdadm` arrays aggregate devices
at the block layer and present one virtual device to a filesystem. That
can provide performance and/or redundancy depending on RAID level, but it
also means the filesystem depends on the array layout. mergerfs does not
provide block-level RAID; it pools already-formatted filesystems and can
be combined with tools like SnapRAID or NonRAID when redundancy is needed.
* **Better than mergerfs for:** kernel software RAID with real-time
redundancy and array-level performance tuning.
* **Worse than mergerfs for:** flexible mixed-size drive pooling where
per-filesystem independence and simple incremental expansion matter.
## device mapper
* [https://en.wikipedia.org/wiki/Device_mapper](https://en.wikipedia.org/wiki/Device_mapper)
* [https://docs.kernel.org/admin-guide/device-mapper](https://docs.kernel.org/admin-guide/device-mapper)
Device mapper is a kernel framework for creating virtual block devices
from one or more underlying devices. Technologies such as LVM,
`dm-crypt`, `dm-cache`, `dm-thin`, and multipath are built on top of it.
Because device mapper works at the block layer, it is not directly
comparable to mergerfs. It is complementary: you can use device mapper
for block-level functionality (encryption, caching, thin provisioning,
etc.) and then place filesystems on those devices, which mergerfs can
later pool if desired.
* **Better than mergerfs for:** block-layer transformations such as
transparent encryption, caching, and provisioning features.
* **Worse than mergerfs for:** presenting one unified pathname-level view
across many independent, already-formatted filesystems.
## UnRAID
* [https://unraid.net](https://unraid.net)
UnRAID is a full OS and offers a (FUSE based?) filesystem which
provides a union of filesystems like mergerfs but with the addition of
live parity calculation and storage. Outside parity calculations
mergerfs offers more features and due to the lack of real-time parity
calculation can have higher peak performance. Some users also prefer
an open source solution.
UnRAID is a full OS platform that offers pooled storage with integrated
real-time parity. Compared with mergerfs, its main advantage is that it
bundles pooling and parity in one product, while mergerfs is a
standalone Linux pooling filesystem without built-in redundancy. For
some users mergerfs' Linux distro-agnostic and fully open-source
approach is also preferable.
For semi-static data mergerfs + [SnapRAID](http://www.snapraid.it)
For semi-static data, mergerfs + [SnapRAID](https://www.snapraid.it)
provides a similar, but not real-time, solution.
[NonRAID](https://github.com/qvr/nonraid) (see below) is a
fork of UnRAID's parity calculation solution and can also be used with
mergerfs.
* **Better than mergerfs for:** turnkey pooling plus integrated real-time
parity in one product.
* **Worse than mergerfs for:** users who prefer a Linux distro-agnostic,
fully open-source component approach.
## NonRAID
@@ -152,160 +278,337 @@ single device. Each device gets a virtual block device associated with
it which can be formatted with the filesystem of the user's choice and
will be individually accessible. Similar to SnapRAID but real-time.
Compared with mergerfs, NonRAID adds kernel-level real-time parity while
still allowing per-disk filesystems, whereas mergerfs focuses on pooling
views and placement policies without parity.
Given each device has its own filesystem like a traditional setup it can
be used with mergerfs for those looking for a unified view.
* **Better than mergerfs for:** real-time parity protection without
traditional RAID striping behavior.
* **Worse than mergerfs for:** users who want the simplest setup and do
not need parity, since it adds operational complexity.
## ZFS
* [https://en.wikipedia.org/wiki/ZFS](https://en.wikipedia.org/wiki/ZFS)
mergerfs is very different from ZFS. mergerfs is intended to provide
flexible pooling of arbitrary filesystems (local or remote), of
arbitrary sizes, and arbitrary filesystems. Particularly in `write
once, read many` use cases such as bulk media storage. Where data
integrity and backup is managed in other ways. In those use cases ZFS
can introduce a number of costs and limitations as described
[here](http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html),
[here](https://markmcb.com/2020/01/07/five-years-of-btrfs/), and
[here](https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
ZFS is an advanced filesystem that combines storage pooling,
snapshots, data integrity checking, and built-in RAID-like redundancy
all in one package. It offers features like transparent compression,
encryption, and automatic data repair.
Compared with mergerfs, ZFS is a full storage stack that manages disks
directly with integrated integrity and redundancy features, while
mergerfs is a lightweight FUSE layer that pools existing filesystems
without built-in redundancy.
* **Better than mergerfs for:** integrated data integrity, snapshots,
checksumming, and built-in redundancy.
* **Worse than mergerfs for:** low-overhead incremental pooling of
existing filesystems with minimal planning.
## ZFS AnyRAID
[ZFS
AnyRAID](https://hexos.com/blog/introducing-zfs-anyraid-sponsored-by-eshtek)
is a feature being developed for ZFS which is intended to provide more
flexibility in ZFS pools. Allowing a mix of capacity disks to have
greater capacity than traditional RAID and would allow for partial
upgrades while keeping live redundancy.
* [https://github.com/openzfs/zfs/discussions/16773](https://github.com/openzfs/zfs/discussions/16773)
This ZFS feature, as of mid-2025, is extremely early in its development
and there are no timelines or estimates for when it may be released.
[ZFS AnyRAID](https://github.com/openzfs/zfs/discussions/16773)
([announcement](https://hexos.com/blog/introducing-zfs-anyraid-sponsored-by-eshtek))
is a feature under development for ZFS intended to provide more
flexibility in pools by allowing mixed-capacity disks while
maintaining live redundancy.
If released, ZFS AnyRAID would offer some of the flexibility associated
with mergerfs-style mixed-capacity expansion, but with built-in
redundancy inside ZFS. mergerfs already provides that mixed-size,
add-drives-anytime flexibility today, albeit without built-in
redundancy. If you need redundancy with that flexibility, ZFS AnyRAID
could become an option when available; until then, mergerfs remains the
simpler currently available choice for mixed-capacity pooling, with
redundancy and integrity available via SnapRAID and/or NonRAID.
* **Better than mergerfs for:** users who want mixed-capacity flexibility
and built-in live redundancy in one filesystem stack.
* **Worse than mergerfs for:** users who need mature availability now and
the simplest add-drives-anytime workflow without redundancy overhead.
## Bcachefs
* [https://bcachefs.org](https://bcachefs.org)
Bcachefs is a modern copy-on-write filesystem for Linux designed for
multi-device storage aggregation. It includes integrated features such
as checksumming, snapshots, compression, encryption, and redundancy.
Compared with mergerfs, bcachefs is a full integrated filesystem with
built-in checksumming, snapshots, compression, and redundancy, while
mergerfs is a lightweight pooling layer over existing filesystems.
Bcachefs is still evolving and should be evaluated carefully for
production use.
* **Better than mergerfs for:** integrated CoW filesystem features,
checksumming, snapshots, compression, and built-in multi-device
redundancy.
* **Worse than mergerfs for:** conservative production setups prioritizing
maturity, simplicity, and broad long-term deployment stability.
## Btrfs Single Data Profile
* [https://btrfs.readthedocs.io](https://btrfs.readthedocs.io)
[Btrfs'](https://btrfs.readthedocs.io) `single` data profile is a
multi-device Btrfs mode that can spread data across drives without
adding redundancy.
Compared with mergerfs, Btrfs `single` is managed as one integrated
filesystem, while mergerfs pools separate existing filesystems. In
Btrfs `single`, losing one device can impact the whole filesystem;
with mergerfs, loss is typically limited to data on the failed disk.
* **Better than mergerfs for:** integrated multi-device management in one
filesystem namespace.
* **Worse than mergerfs for:** users who value per-disk failure isolation
and easy independent recovery.
## StableBit's DrivePool
* [https://stablebit.com](https://stablebit.com)
DrivePool works only on Windows so not as common an alternative as
other Linux solutions. If you want to use Windows then DrivePool is a
good option. Functionally the two projects work a bit
differently. DrivePool always writes to the filesystem with the most
free space and later rebalances. mergerfs does not currently offer
rebalance but chooses a branch at file/directory create
time. DrivePool's rebalancing can be done differently in any directory
and has file pattern matching to further customize the
behavior. mergerfs, not having rebalancing does not have these
features, but similar features are planned for mergerfs v3. DrivePool
has builtin file duplication which mergerfs does not natively support
(but can be done via an external script.)
DrivePool is a commercial storage pooling product for Windows that
combines multiple drives into a single volume and includes balancing
and optional duplication capabilities.
There are a lot of misc differences between the two projects but most
features in DrivePool can be replicated with external tools in
combination with mergerfs.
Compared with mergerfs, DrivePool emphasizes integrated balancing and
duplication features in a Windows-first product, while mergerfs
emphasizes Linux FUSE pooling with policy-based create placement and
external-tool composability.
Additionally, DrivePool is a closed source commercial product vs
mergerfs a ISC licensed open source project.
* **Better than mergerfs for:** Windows users needing built-in
rebalancing and native file duplication features.
* **Worse than mergerfs for:** Linux-native deployments and users who
prefer open-source tooling and composable external utilities.
## Plan9 binds
## Windows Storage Spaces
* [https://learn.microsoft.com/en-us/windows-server/storage/storage-spaces/overview](https://learn.microsoft.com/en-us/windows-server/storage/storage-spaces/overview)
Windows Storage Spaces is Microsoft's software-defined storage
platform for Windows Server and Windows Pro. It aggregates multiple
physical disks into logical storage pools with built-in redundancy
options (mirroring, parity) and automatic performance optimization
through tiered caching.
Storage Spaces is Windows-only and requires more planning upfront due
to redundancy options and RAID-like features. mergerfs is
Linux-focused and takes a simpler approach, pooling existing
filesystems without built-in redundancy. If you need a Windows
solution with integrated data protection, Storage Spaces is a good
choice. For Linux users seeking lightweight pooling without redundancy
overhead, mergerfs is the better option.
* **Better than mergerfs for:** Windows environments needing integrated
pooling, resilience, and tiering in the platform stack.
* **Worse than mergerfs for:** Linux hosts and lightweight pooling where
minimal overhead and per-filesystem independence are preferred.
## Plan 9 binds
* [https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs#Union_directories_and_namespaces](https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs#Union_directories_and_namespaces)
Plan9 has the native ability to bind multiple paths/filesystems
together to create a setup similar to simplified union
filesystem. Such bind mounts choose files in a "first found" in the
order they are listed similar to mergerfs' `ff` policy. Similar, when
creating a file it will be created on the first directory in the
union.
Plan 9 has a native bind mechanism that can compose paths and
filesystems into a single namespace view, including union-like
directory behavior.
Compared with mergerfs, Plan 9 binds are an OS-native namespace feature
with simple ordered behavior, while mergerfs provides Linux-focused
filesystem pooling with a larger set of configurable branch policies.
Plan 9 isn't a widely used OS so this comparison is mostly academic.
* **Better than mergerfs for:** native Plan 9 environments where
namespace composition is a core OS feature.
* **Worse than mergerfs for:** Linux users due to ecosystem relevance and
practical deployment availability.
## SnapRAID pooling
* [https://www.snapraid.it/manual](https://www.snapraid.it/manual)
[SnapRAID](https://www.snapraid.it/manual) has a pooling feature which
creates "a read-only virtual view of all the files in your array using
symbolic links."
As mentioned in the description this "view" is just the creation of
Compared with mergerfs, SnapRAID pooling is a symlink-generated,
read-only view refreshed by SnapRAID operations, while mergerfs is a
live mounted filesystem that supports normal read/write behavior.
As mentioned in the description, this "view" is just the creation of
the same directory layout with symlinks to all files. This means that
reads (and writes) to files are at native speeds but limited in that
it can not practically be used as a target for writing new files and
is only updated when `snapraid pool` is run. Note that some software
treat symlinks differently than regular files. For instance some
backup software will skip symlinks by default.
reads (and writes) to files are at native speeds, but it is not
practical to use as a target for writing new files, and it is only
updated when `snapraid pool` is run. Note that some software treats
symlinks differently than regular files. For instance, some backup
software will skip symlinks by default.
mergerfs has the feature [symlinkify](config/symlinkify.md) which
provides a similar behavior but is more flexible in that it is not
read-only. That said there can still be some software that won't like
that kind of setup.
* **Better than mergerfs for:** read-only, native-speed directory views
of SnapRAID-managed content.
* **Worse than mergerfs for:** general read/write pooled filesystem use
and always-live view updates.
## rclone union
* [https://rclone.org/union](https://rclone.org/union)
rclone's [union](https://rclone.org/union) backend allows you to
create a union of multiple rclone backends and was inspired by
[mergerfs](https://rclone.org/union/#behavior-policies). Given rclone
knows more about the underlying backend than mergerfs could it can be
more efficient than creating a similar union with `rclone mount` and
mergerfs.
[mergerfs](https://rclone.org/union/#behavior-policies). Because
rclone knows more about the underlying backends than mergerfs can, it
can be more efficient than creating a similar union with `rclone mount`
and mergerfs.
However, it is not uncommon to see users setup rclone mounts and
Compared with mergerfs, rclone union is backend-aware and optimized for
cloud/remote storage abstractions, while mergerfs is optimized for local
POSIX filesystem pooling and branch policies.
However, it is not uncommon to see users set up rclone mounts and
combine them with local or other remote filesystems using mergerfs
given the differing feature sets and focuses of the two projects.
* **Better than mergerfs for:** remote-backend-heavy workloads where
backend-aware behavior improves cloud storage handling.
* **Worse than mergerfs for:** local POSIX pooling scenarios where direct
filesystem semantics and broad local tool compatibility matter.
## distributed filesystems
* AFS
* Ceph/CephFS
* GlusterFS
* LizardFS
* MooseFS
* [AFS](https://www.openafs.org)
* [Ceph/CephFS](https://ceph.io)
* [GlusterFS](https://www.gluster.org)
* [LizardFS](https://lizardfs.com)
* [MooseFS](https://moosefs.com)
* etc.
Distributed remote filesystems come in many forms. Some offering POSIX
filesystem compliance and some not. Some providing remote block
Distributed remote filesystems come in many forms. Some offer POSIX
filesystem compliance and some do not. Some provide remote block
devices or object stores on which a POSIX or POSIX-like filesystem is
built on top of. Some which are effectively distributed union
filesystems with duplication.
built. Some are effectively distributed union filesystems with
duplication.
These filesystems almost always require a significant amount of
compute to run well and are typically deployed on their own
hardware. Often in an "orchestrators" + "workers" configuration across
numerous nodes. This limits their usefulness for casual and homelab
users. There could also be issues with network congestion and general
performance if using a single network and that network is slower than
the storage devices.
Compared with mergerfs, distributed filesystems focus on multi-node
storage, replication, and cluster behavior, while mergerfs focuses on
single-host local pooling of existing filesystems.
While possible to use a distributed filesystem to combine storage
devices (and typically to provide redundancy) it will require a more
complicated setup and more compute resources than mergerfs (while also
offering a different set of capabilities.)
* **Better than mergerfs for:** multi-node or geographically distributed
workloads with built-in replication and high availability.
* **Worse than mergerfs for:** single-host and homelab pooling where low
overhead and operational simplicity are key.
## nofs
* [https://github.com/chapmanjacobd/nofs](https://github.com/chapmanjacobd/nofs)
nofs is a tool that provides mergerfs-like functionality for combining
multiple filesystems/directories into a unified view but does so
entirely through subcommands which replicate traditional POSIX
commands rather than providing a proper filesystem. It takes a lot of
inspiration from mergerfs in that it supports policy-based branch
selection (mfs, ff, pfrd, epmfs, and others), read-only and no-create
branch modes, and recreates POSIX-like commands such as `ls`, `find`,
`which`, `cp`, `mv`, and `rm`.
Compared with mergerfs, nofs is command-driven rather than a mounted
filesystem, so mergerfs is the option for transparent system-wide
pooling through normal POSIX filesystem calls.
Given its design nofs is not suited for general usage as third-party
applications will not be able to take advantage of the unioning
behavior it offers. It is primarily for more simple situations where
something like mergerfs is unable to be used.
* **Better than mergerfs for:** constrained environments where a FUSE
mount is not possible but command-style union behavior is acceptable.
* **Worse than mergerfs for:** transparent system-wide filesystem pooling
usable by arbitrary third-party applications.
## policyfs
* [https://policyfs.org](https://policyfs.org)
* [https://github.com/hieutdo/policyfs](https://github.com/hieutdo/policyfs)
policyfs is a Linux FUSE storage daemon that unifies multiple storage
paths under a single mountpoint, similarly to mergerfs. Its
distinguishing features are explicit path-pattern-based routing rules
for placement, and an optional SQLite metadata index that can serve
`readdir` and `getattr` operations without spinning up HDDs.
Compared with mergerfs, policyfs emphasizes explicit path-based routing
and optional metadata indexing, while mergerfs emphasizes a broad,
policy-driven pooling model with longer operational history and wider
adoption. It doesn't try limiting spinup as doing so fundimentally is
in [conflict with other features.](faq/limit_drive_spinup.md)
Due to the nature of PolicyFS it can not really be used while changing
files out-of-band and with the delayed writeback of metadata and
unlinking there is a greater risk of inconsistancy in error
conditions.
* **Better than mergerfs for:** explicit path-based routing and optional
metadata indexing to reduce HDD spin-ups.
* **Worse than mergerfs for:** users prioritizing project maturity,
adoption, performance, and long-established operational behavior.
## Greyhole
* [https://www.greyhole.net](https://www.greyhole.net)
* [https://github.com/gboudreau/Greyhole](https://github.com/gboudreau/Greyhole)
Greyhole is an open-source storage pooling application that works via
Samba. Rather than implementing a FUSE filesystem, it hooks into Samba
VFS to intercept file operations and distribute files across multiple
drives, with optional per-share redundancy.
Compared with mergerfs, Greyhole is Samba-centric and includes
integrated share-level redundancy, while mergerfs is a general-purpose
Linux FUSE mount for local pooling via standard POSIX filesystem calls.
* **Better than mergerfs for:** Samba-centric environments that need
built-in per-share redundancy in the same tool.
* **Worse than mergerfs for:** general-purpose local filesystem pooling
outside Samba and heavy small-file churn workloads.
## 9P
[9P, the Plan 9 Filesystem
Protocol,](https://en.wikipedia.org/wiki/9P_(protocol)) is a protocol
developed for the Plan 9 operating system to help expand on the Unix
idea that everything should be a file. The protocol made its way to
other systems and is still widely used. As such 9P is not directly
comparable to mergerfs but more so to FUSE which mergerfs uses. FUSE
is also a filesystem protocol (though designed for kernel <->
userspace communication rather than over a network). FUSE, even more
than the
[9P2000.L](https://github.com/chaos/diod/blob/master/protocol.md)
variant of 9P, is focused primarily on supporting Linux filesystem
features.
* [https://en.wikipedia.org/wiki/9P_(protocol)](https://en.wikipedia.org/wiki/9P_(protocol))
mergerfs leverages FUSE but could have in theory leveraged 9P with a
reduction in features.
[9P](https://en.wikipedia.org/wiki/9P_(protocol)) is a filesystem
protocol from the Plan 9 operating system used to access files over a
network between systems.
While 9P has [extensive
usage](https://docs.kernel.org/filesystems/9p.html) in certain
situations its use in modern userland Linux systems is limited. FUSE
has largely replaced use cases that may have been implemented with 9P
servers in the past.
Compared with mergerfs, 9P is a network filesystem protocol, while
mergerfs is a local Linux FUSE filesystem for pooling directories and
filesystems under one mount with branch policies.
* **Better than mergerfs for:** lightweight network filesystem protocol
use between compatible systems.
* **Worse than mergerfs for:** local Linux filesystem pooling because it
does not provide mergerfs-like branch policies or pooling semantics.
+31 -18
View File
@@ -1,4 +1,4 @@
# QuickStart
# Quick Start
## Install
@@ -25,24 +25,22 @@ cases.
* func.getattr=newest
* dropcacheonclose=false
In previous versions of Linux it was unable to support `mmap` if [page
caching](config/cache.md) was disabled (ie:
`cache.files=off`). However, it now will enable page caching if needed
for a particular file if `mmap` is requested.
See [Known Issues and Bugs#Software Using
mmap](known_issues_bugs.md#software-using-mmap) for more details why
the Linux and mergerfs version matter and its relation to `mmap`.
[mmap](https://en.wikipedia.org/wiki/Mmap) is needed by certain
software to read and write to a file. However, many software could
work without it and fail to have proper error handling for when it is
unavailable. Many programs that use **sqlite3** will require `mmap`
despite [sqlite3 working perfectly
fine](known_issues_bugs.md#sqlite3-plex-jellyfin-do-not-work-with-mergerfs)
without it (and in some cases can be more performant with regular file
IO.)
**NOTE:** This "auto mmap" feature is only supported in mergerfs
v2.41.0 and above. For older versions see below or the docs from that
release.
### You use Linux v6.5 or below
#### You need `mmap` (used by rtorrent and many sqlite3 base software)
#### You need `mmap`
To keep the details in a single location: see [Known Issues and
Bugs#Software Using mmap](known_issues_bugs.md#software-using-mmap)
for more details.
* cache.files=auto-full
* category.create=pfrd
@@ -66,6 +64,15 @@ branches](config/branches.md#branch-setup).
### Command Line
```
# Two positional options: `:` colon separated branches and mountpoint
mergerfs -o opt[,opt...] /branch0:/branch1 /mountpoint
# or list of positional options where the last is the mountpoint
mergerfs -o opt[,opt...] /branch0 /branch1 /mountpoint
# or set mountpoint via -o mountpoint
mergerfs -o mountpoint=/mountpoint /branch0 /branch1
```
```
mergerfs -o cache.files=off,category.create=pfrd,func.getattr=newest,dropcacheonclose=false /mnt/hdd0:/mnt/hdd1 /media
```
@@ -131,9 +138,12 @@ ExecStart=/usr/bin/mergerfs \
-o category.create=pfrd \
-o func.getattr=newest \
-o dropcacheonclose=false \
/mnt/hdd0:/mnt/hdd1 \
/mnt/hdd0 \
/mnt/hdd1 \
/media
ExecStop=/bin/fusermount -uz /media
ExecStop=/usr/bin/umount /media
# Or if you need fusermount
# ExecStop=/usr/bin/mergerfs-fusermount -u /media
Restart=on-failure
[Install]
@@ -195,9 +205,12 @@ ExecStart=/usr/bin/mergerfs \
-o category.create=pfrd \
-o func.getattr=newest \
-o dropcacheonclose=false \
/mnt/hdd0:/mnt/hdd1 \
/mnt/hdd0 \
/mnt/hdd1 \
/media
ExecStop=/bin/fusermount -uz /media
ExecStop=/usr/bin/umount /media
# Or if you need fusermount
# ExecStop=/usr/bin/mergerfs-fusermount -u /media
Restart=on-failure
[Install]
+23 -15
View File
@@ -2,9 +2,9 @@
## Projects using mergerfs
* [Lakka.tv](https://lakka.tv/): A turnkey software emulation Linux
* [Lakka.tv](https://lakka.tv): A turnkey software emulation Linux
distribution. Used to pool user and local storage. Also includes my
other project [Opera](https://retroarch.com/). A 3DO emulator.
other project [Opera](https://retroarch.com). A 3DO emulator.
* [OpenMediaVault](https://www.openmediavault.org): A network attached
storage (NAS) solution based on Debian Linux. They provide plugins
to manage mergerfs.
@@ -13,28 +13,34 @@
mergerfs to create pools from existing filesystems.
* [ZimaOS](https://github.com/IceWhaleTech/zimaos-rauc): A more
commercially focused NAS OS by the authors of CasaOS at [Ice
Whale](https://www.zimaboard.com/).
* [Cosmos Cloud](https://cosmos-cloud.io/): Cosmos "take the chore out
Whale](https://www.zimaboard.com).
* [Cosmos Cloud](https://cosmos-cloud.io): Cosmos "take the chore out
of selfhosting, with automated maintenance and fully secured setup
out of the box. It even integrates to your existing setup."
* [MOS](https://mos-official.net): "Lightweight, modular server OS for
NAS, Containers & VMs."
## Software and services commonly used with mergerfs
* [snapraid](https://www.snapraid.it/): a backup program designed for
* [snapraid](https://www.snapraid.it): a backup program designed for
disk arrays, storing parity information for data recovery in the
event of up to six disk failures.
* [rclone](https://rclone.org/): a command-line program to manage
* [nonraid](https://github.com/qvr/nonraid): NonRAID is a fork of the
unRAID system's open-source md_unraid kernel driver enabling
UnRAID-style storage arrays with parity protection outside of the
commercial UnRAID system.
* [rclone](https://rclone.org): a command-line program to manage
files on cloud storage. It is a feature-rich alternative to cloud
vendors' web storage interfaces. rclone's
[union](https://rclone.org/union/) feature is based on mergerfs
[union](https://rclone.org/union) feature is based on mergerfs
policies.
* [ZFS](https://openzfs.org/): A popular filesystem and volume
* [ZFS](https://openzfs.org): A popular filesystem and volume
management platform originally part of Sun Solaris and later ported
to other operating systems. It is common to use ZFS with
mergerfs. ZFS for important data and mergerfs pool for replacable
media.
* [Proxmox](https://www.proxmox.com/): Proxmox is a popular, Debian
* [Proxmox](https://www.proxmox.com): Proxmox is a popular, Debian
based, virtualization platform. Users tend to install mergerfs on
the host and pass the mount into containers.
* [UnRAID](https://unraid.net): "Unraid is a powerful, easy-to-use
@@ -42,9 +48,9 @@
storage." While UnRAID has its own union filesystem it isn't
uncommon to see UnRAID users leverage mergerfs given the differences
in the technologies. There is a [plugin available by
Rysz](https://forums.unraid.net/topic/144999-plugin-mergerfs-for-unraid-support-topic/)
Rysz](https://forums.unraid.net/topic/144999-plugin-mergerfs-for-unraid-support-topic)
to ease installation and setup.
* [TrueNAS SCALE](https://www.truenas.com/truenas-scale/): An
* [TrueNAS SCALE](https://www.truenas.com/truenas-scale): An
enterprise focused NAS operating system with OpenZFS support. A Some
users are requesting mergerfs be [made part
of](https://forums.truenas.com/t/add-unionfs-or-mergerfs-and-rdam-enhancement-then-beat-all-other-nas-systems/23218)
@@ -54,7 +60,7 @@
details [on their
wiki](https://cloudboxes.io/wiki/how-to/apps/set-up-mergerfs-using-ssh):
on how to setup mergerfs.
* [QNAP](https://www.myqnap.org/product/mergerfs-apache83/): A company
* [QNAP](https://www.myqnap.org/product/mergerfs-apache83): A company
known for their turnkey, consumer focused NAS devices. Someone has
created builds of mergerfs for different QNAP devices.
@@ -67,11 +73,13 @@ Linux](https://repology.org/project/mergerfs/versions) distributions
and FreeBSD.
Note: Any non-rolling release based distro is likely to have
out-of-date versions.
out-of-date versions. `deb` and `rpm` based distro users should
probably use the [mergerfs project's provided
packages.](https://github.com/trapexit/mergerfs/releases)
* [Debian](https://packages.debian.org/bullseye/mergerfs)
* [Debian](https://packages.debian.org/search?keywords=mergerfs&searchon=names&suite=all&section=all)
* [Ubuntu](https://launchpad.net/ubuntu/+source/mergerfs)
* [Fedora](https://rpmsphere.github.io/)
* [Fedora](https://rpmsphere.github.io)
* [T2](https://t2sde.org/packages/mergerfs)
* [Alpine](https://pkgs.alpinelinux.org/packages?name=mergerfs&branch=edge&repo=&arch=&maintainer=)
* [Gentoo](https://packages.gentoo.org/packages/sys-fs/mergerfs)
+33 -14
View File
@@ -49,6 +49,23 @@ NFS export settings:
running mergerfs as a non-root user, there is no guarantee that
mergerfs will be able to do what it needs to manage the underlying
filesystem.
* `softerr` or `soft`: A perhaps questionable choice but if NFS
freezes up because the backend dies or the network goes down the
default `hard` mount would cause mergerfs to block the same as any
other software. By setting `softerr` or `soft` the NFS client will
timeout eventually and return an error.
* `softreval`: NFS client will serve up cached data after `retrans`
attempts to revalidate the data. Helps with intermittent network
issues.
* `timeo=150`: Timeout till retrying request.
* `retrans=3`: Number of retrying a request.
* `rsize=131072`: Read size.
* `wsize=131072`: Write size.
`no_root_squash` is necessary but the others are just suggestions and
you may want to alter based on your use case. The risk of using `hard`
and other more strict behavior options is that should NFS completely
lock up it will impact mergerfs negatively.
### NFS exporting mergerfs
@@ -59,18 +76,20 @@ but if you run into problems it may be worth trying Samba/SMB.
**mergerfs settings:**
* `noforget`
* `never-forget-nodes=true` or `remember-nodes=SECONDS`
* `inodecalc=path-hash`
* `lazy-umount-mountpoint=false`
`noforget` is needed because NFS uses the `name_to_handle_at` and
`open_by_handle_at` functions which allow a program to keep a
reference to a file without technically having it open in the typical
sense. The problem is that FUSE has no way to know that NFS has a
handle that it will later use to open the file again. As a result, it
is possible for the kernel to tell mergerfs to forget about the file
node and should NFS ever ask for that node's details in the future it
would have nothing to respond with. Keeping nodes around forever is
`never-forget-nodes=true` (or `remember-nodes` with a large value such
as `86400` for a full day) is needed because NFS uses the
`name_to_handle_at` and `open_by_handle_at` functions which allow a
program to keep a reference to a file without technically having it
open in the typical sense. The problem is that FUSE has no way to know
that NFS has a handle that it will later use to open the file
again. As a result, it is possible for the kernel to tell mergerfs to
forget about the file node and should NFS ever ask for that node's
details in the future it would have nothing to respond with. Keeping
nodes around forever (or however long is set with `remember-nodes`) is
not ideal but at the moment the only way to manage the situation.
`inodecalc=path-hash` is needed because NFS is sensitive to
@@ -101,11 +120,11 @@ value. An easy way to generate a random value is to use the command
line tool `uuid` or `uuidgen` or through a website such as
[uuidgenerator.net](https://www.uuidgenerator.net/).
`no_root_squash` is required for the same reason mergerfs needs to run
as `root`. Certain behaviors of mergerfs require control over the
filesystem that only `root` can preform. If squashing is enabled, or
mergerfs was running as non-root, it would be unable to perform
certain function and you will receive permission errors.
`no_root_squash` is not *technically* required but suggested. If NFS
is root squashing then any request that an app running as root makes
will show up as a different user to mergerfs. This is perfectly fine
so long as the client software using NFS are not running as root and
expect to have root like privileges.
## SMB / CIFS
+5 -5
View File
@@ -9,11 +9,10 @@
* readdir concurrency
* memory
* 1MB+ pre reader thread + inflight processing for messages
depending on [fuse_msg_size](config/fuse_msg_size.md)
depending on [fuse-msg-size](config/fuse-msg-size.md)
* buffers allocated temporarily for reading directories
* [gidcache](faq/technical_behavior_and_limitations.md#how-does-mergerfs-handle-credentials)
* FUSE nodes
* noforget forgotten nodes
* never-forget-nodes=true nodes
## Management
@@ -24,8 +23,9 @@
value to -990.
* mergerfs increases [its available file descriptor and file size
limit.](https://www.man7.org/linux/man-pages/man3/setrlimit.3p.html)
* mergerfs lowers its [scheduling
* mergerfs sets its [scheduling
priority](https://man7.org/linux/man-pages/man3/setpriority.3p.html)
to -10 ([by default](config/options.md))
(nice value) to -10 ([by default](config/options.md)), raising its
CPU scheduling priority above the default of 0.
* The [readahead](config/readahead.md) values of mergerfs itself and
managed filesystems can be modified.
+3 -5
View File
@@ -35,7 +35,7 @@ configure the mounting of mergerfs (/etc/fstab, systemd, etc.).
### Command Line Tooling
Extended attributes is prevelant enough that there are common tools
Extended attributes are prevalent enough that there are common tools
available for interacting with them.
In Debian / Ubuntu distributions you can get the tools
@@ -122,14 +122,14 @@ user.mergerfs.category.search="mfs"
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.create .mergerfs
user.mergerfs.category.search="prfd"
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.branches -v "'+</mnt/c=RO .mergerfs
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.branches -v '+</mnt/c=RO' .mergerfs
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.branches .mergerfs
user.mergerfs.branches="/mnt/c=RO:/mnt/a=RW:/mnt/b=RW"
```
### Commands
There are a number of commands / behaviors which can be triggerd by
There are a number of commands / behaviors which can be triggered by
writing ([setfattr](https://linux.die.net/man/1/setfattr),
[setxattr](https://linux.die.net/man/2/setxattr)) particular xattr
keys of `/mountpoint/.mergerfs`. These keys do not show up in key
@@ -143,8 +143,6 @@ allows a value.
| user.mergerfs.cmd.gc | (empty) |Trigger a thorough garbage collection of certain pools of resources. The xattr value is not used. |
| user.mergerfs.cmd.gc1 | (empty) | Trigger a simple garbage collection of certain pools of resources. This is also done on a timer. |
| user.mergerfs.cmd.invalidate-all-nodes | (empty) | Attempts to invalidate FUSE file nodes. Primarily used for debugging. |
| user.mergerfs.cmd.invalidate-gid-cache | (empty) | Invalidates all entries in gid cache. Primarily used for debugging but can also be useful in cases where gid supplemental groups change. |
| user.mergerfs.cmd.clear-gid-cache | (empty) | Clears all entries in the gid cache. Primarily used for debugging but can also be useful in cases where gid supplemental groups change. |
```
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.cmd.gc /mnt/mergerfs/.mergerfs
+43 -1
View File
@@ -44,6 +44,16 @@ sudo dpkg -i mergerfs_<ver>.debian-<rel>_<arch>.deb
#### Ubuntu
Given non-LTS releases are rarely used for servers and only have [9
months of support](https://ubuntu.com/about/release-cycle) we do not
build packages for them by default. It is recommended that you first
try the `deb` file for the LTS release most closely related to your
non-LTS release. If that does not work you can use the [static
build](#static-linux-binaries) or [file a
ticket](https://github.com/trapexit/mergerfs/issues) requesting a
package built.
```
wget https://github.com/trapexit/mergerfs/releases/download/<version>/mergerfs_<ver>.ubuntu-<rel>_<arch>.deb
sudo dpkg -i mergerfs_<ver>.ubuntu-<rel>_<arch>.deb
@@ -66,7 +76,7 @@ sudo rpm -i mergerfs-<ver>.fc<rel>.<arch>.rpm
## SUSE/OpenSUSE
Avaliable at [filesystems repo](https://build.opensuse.org/package/show/filesystems/mergerfs)
Available at [filesystems repo](https://build.opensuse.org/package/show/filesystems/mergerfs)
if you have not added the repo (e.g. in case of OpenSUSE Tumbleweed, rel=openSUSE_Tumbleweed):
@@ -96,6 +106,7 @@ nix-env -iA nixos.mergerfs
[Check your distro.](../related_projects.md#distributions-including-mergerfs)
## Static Linux Binaries
If your distro does not package mergerfs there are static binaries
@@ -107,3 +118,34 @@ Get the tarball from the [releases page](https://github.com/trapexit/mergerfs/re
wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-static-linux_<arch>.tar.gz
sudo tar xvf mergerfs-static-linux_<arch>.tar.gz -C /
```
## Podman, Docker, OCI Containers
[https://github.com/trapexit/mergerfs/pkgs/container/mergerfs](https://github.com/trapexit/mergerfs/pkgs/container/mergerfs)
```
podman pull ghcr.io/trapexit/mergerfs:TAG
# or
docker pull ghcr.io/trapexit/mergerfs:TAG
```
### rootful container runtimes
```
docker run --device=/dev/fuse --cap-add=SYS_ADMIN -v /mnt/to-merge:/mnt/to-merge:rshared -v /mnt/mergerfs:/mnt/mergerfs:z,shared ghcr.io/trapexit/mergerfs:TAG -f '/mnt/to-merge/*' '/mnt/mergerfs'
```
* `--device=/dev/fuse`: Pass in host FUSE device.
* `--cap-add=SYS_ADMIN`: Give proper permissions to mount.
* `-v /mnt/mergerfs:/mnt/mergerfs:z,shared`: `z` and `shared` will
allow the mount to be shared between the container and host and
therefore other containers.
* `-v /mnt/to-merge:/mnt/to-merge:rshared`: The mounts from the host
to merge within the container.
### rootless container runtimes
Not possible? `:shared` is not rejected but won't work the same with
rootless. Podman doesn't allow the creation of pods with shared mount
namespace in rootless mode.
+4 -4
View File
@@ -1,4 +1,4 @@
# Sponsorship and Donations
# Sponsorship, Donations, Support Contracts
[https://github.com/trapexit/support](https://github.com/trapexit/support)
@@ -12,6 +12,6 @@ and would like to support the project financially it would be very
much appreciated.
If you are using mergerfs commercially please consider sponsoring the
project to ensure it continues to be maintained and receive
updates. If custom features are needed feel free to [contact me
directly](mailto:support@spawn.link).
project or purchasing a support contract to ensure it continues to be
maintained and receive updates. [Reach out for more
details](mailto:support@spawn.link?subject=mergerfs+support+contract+inquiry).
+23 -17
View File
@@ -24,31 +24,37 @@ directly.](mailto:support@spawn.link)**
* Solution already ruled out and why.
* The details from the output of the
[mergerfs.collect-info](tooling.md#mergerfscollect-info) tool.
* Alternatively:
* Version of mergerfs: `mergerfs --version`
* mergerfs settings / arguments: from fstab, systemd unit, command
line, OMV plugin, etc.
* Version of the OS: `uname -a` and `lsb_release -a`
* List of branches, their filesystem types, sizes (before and after
issue): `df -h`, `lsblk -o NAME,FSTYPE,FSSIZE,SIZE,MOUNTPOINTS,RM,RO,ROTA`
* **All** information about the relevant paths and files: permissions, ownership, etc.
* **All** information about the application making the requests: version, uid/gid
* **All** information about the relevant paths and files: permissions,
ownership, etc. Some of this is collected by the tool but only the
branch root details.
* **All** information about the application making the requests:
version, uid/gid. Try though to replicate any issues with common
core utilities.
* Try to recreate the problem in the simplest way using standard
programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. With the most simple
mergerfs setup possible such as using only a single branch, well
supported branch filesystem, maybe reduce to using a single thread,
etc.
* Runtime environment:
* Is mergerfs running within a container?
* Are the client apps using mergerfs running in a container?
* A `strace` of the app having problems:
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
* A `strace` of mergerfs while the program is trying to do whatever it
is failing to do:
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
* **Precise** directions on replicating the issue. Do not leave
**anything** out.
* Try to recreate the problem in the simplest way using standard
programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. With the most simple
mergerfs setup possible.
* A `strace` of mergerfs **before** and **while** the program is
trying to do whatever it is failing to do:
* `strace -fvTtt -s 256 -p <mergerfsPID> -o
/tmp/mergerfs.strace.txt`
* If mergerfs is blocking or appears stuck:
* `gdb -q --batch -ex "set pagination off" -ex "thread apply all
bt full" -ex quit --pid <mergerfsPID> > /tmp/mergerfs.stacktrace.txt`
* **Precise** step-by-step directions on replicating the issue. Do not
leave **anything** out.
### Contact / Issue submission
In order of preference.
* github.com: [https://github.com/trapexit/mergerfs/issues](https://github.com/trapexit/mergerfs/issues)
* discord: [https://discord.gg/MpAr69V](https://discord.gg/MpAr69V)
* reddit: [https://www.reddit.com/r/mergerfs](https://www.reddit.com/r/mergerfs)
+5 -4
View File
@@ -6,7 +6,7 @@
like something is not explained sufficiently please [file a
ticket.](https://github.com/trapexit/mergerfs/issues)
* If a `libfuse` argument is not listed it probably shouldn't be used
and are unsupported.
and is unsupported.
* Ensure you're using the latest version. Especially before submitting
bug reports.
* Run mergerfs as `root`. mergerfs is designed and intended to be run
@@ -15,13 +15,14 @@
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 filesystem for out of sync permissions.
* If you still have permission issues be sure you are using POSIX ACL
* If you still have permission issues be sure you are using POSIX
compliant filesystems. mergerfs doesn't generally make exceptions
for FAT, NTFS, or other non-POSIX filesystem.
* Unless using Linux v6.6 or above do **not** use `cache.files=off` if
you expect applications (such as rtorrent) to use
you expect applications to use
[mmap](http://linux.die.net/man/2/mmap). Enabling `dropcacheonclose`
is recommended when `cache.files=auto-full`.
is recommended when `cache.files=auto-full`. See
[quickstart](quickstart.md).
* [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
+6 -6
View File
@@ -50,8 +50,8 @@ OPTIONS:
EXPERIMENTAL
For some time there has been work to enable
[passthrough](config/passthrough.md) IO in FUSE. Passthrough IO would
For some time there has been work to enable IO
[passthrough](config/passthrough.md) in FUSE. IO Passthrough would
allow for near native performance with regards to reads and writes (at
the expense of certain mergerfs features.) In Linux v6.9 that feature
made its way into the kernel however in a somewhat limited form which
@@ -75,7 +75,7 @@ the read/write workflow, such as
[moveonenospc](config/moveonenospc.md), will no longer work.
Also, understand that this will only work on dynamically linked
software (a that dynamically linked with the same general libc version
software (that is dynamically linked with the same general libc version
as the software being used with it.) Anything statically compiled will
not work. Many GoLang and Rust apps are statically compiled.
@@ -83,8 +83,8 @@ The library will not interfere with non-mergerfs filesystems. The
library is written to always fallback to returning the mergerfs opened
file on error.
While the library was written to account for a number of edgecases
there could be some yet accounted for so please report any oddities.
While the library was written to account for a number of edge cases
there could be some yet unaccounted for so please report any oddities.
Thank you to
[nohajc](https://github.com/nohajc/mergerfs-io-passthrough) for
@@ -112,7 +112,7 @@ the same path as found on the host so the preload library can see
them.
**NOTE:** Since a container can have its own OS setup there is no
guarentee that `preload.so` from the host install will be compatible
guarantee that `preload.so` from the host install will be compatible
with the loader found in the container. If that is true it simply
won't work and shouldn't cause any issues.
+32
View File
@@ -0,0 +1,32 @@
#!/bin/sh
set -x
current_branch="$(git branch --show-current)"
mike delete --all --push --allow-empty --ignore-remote-status
git checkout "master"
mike deploy --push "latest" "master" --ignore-remote-status
for tag in $(git tag -l | sort --version-sort --reverse)
do
case "${tag}" in
*-rc*)
continue
;;
esac
if git ls-tree -r --name-only "${tag}" | grep -q 'mkdocs.yml'; then
git checkout "${tag}"
mike deploy --push "${tag}" --ignore-remote-status
fi
done
#latest="$(git tag -l | sort --version-sort --reverse | head -n 1)"
#git checkout "${latest}"
#mike alias --push "${latest}" "latest"
#mike alias --push "latest" "latest"
mike set-default --push "latest" --ignore-remote-status
git checkout "${current_branch}"
+23 -22
View File
@@ -63,8 +63,8 @@ nav:
- setup/installation.md
- setup/upgrade.md
- setup/build.md
- terminology.md
- intro_to_filesystems.md
- Terminology: terminology.md
- Intro to Filesystems: intro_to_filesystems.md
- Config:
- config/options.md
- config/deprecated_options.md
@@ -81,8 +81,9 @@ nav:
- config/inodecalc.md
- config/threads.md
- config/pin-threads.md
- config/link_cow.md
- config/fuse_msg_size.md
- config/proxy-ioprio.md
- config/link-cow.md
- config/fuse-msg-size.md
- config/follow-symlinks.md
- config/link-exdev.md
- config/rename-exdev.md
@@ -94,28 +95,28 @@ nav:
- config/flush-on-close.md
- config/export-support.md
- config/kernel-permissions-check.md
- error_handling_and_logging.md
- resource_usage.md
- runtime_interface.md
- remote_filesystems.md
- tips_notes.md
- known_issues_bugs.md
- project_comparisons.md
- benchmarking.md
- performance.md
- tooling.md
- extended_usage_patterns.md
- config/lazy-umount-mountpoint.md
- Error Handling and Logging: error_handling_and_logging.md
- Resource Usage: resource_usage.md
- Runtime Interface: runtime_interface.md
- Remote Filesystems: remote_filesystems.md
- Tips and Notes: tips_notes.md
- Known Issues and Bugs: known_issues_bugs.md
- Project Comparisons: project_comparisons.md
- Benchmarking: benchmarking.md
- Performance: performance.md
- Tooling: tooling.md
- Extended Usage Patterns: extended_usage_patterns.md
- FAQ:
- faq/why_isnt_it_working.md
- faq/reliability_and_scalability.md
- faq/usage_and_functionality.md
- faq/configuration_and_policies.md
- faq/usage_and_functionality.md
- faq/reliability_and_scalability.md
- faq/compatibility_and_integration.md
- faq/recommendations_and_warnings.md
- faq/technical_behavior_and_limitations.md
- faq/have_you_considered.md
- faq/limit_drive_spinup.md
- related_projects.md
- media_and_publicity.md
- support.md
- sponsorship_and_donations.md
- Related Projects: related_projects.md
- Media and Publicity: media_and_publicity.md
- Support: support.md
- Sponsorship and Donations: sponsorship_and_donations.md
+27 -1
View File
@@ -1,5 +1,7 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
ISC License
Copyright (c) 2026, 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
@@ -28,3 +30,27 @@ namespace assert
struct StaticAssert<true>
{};
}
#ifdef NDEBUG
#define ASSERT_NOT_NULL(ptr)
#else
#include <cassert>
#include <iostream>
#define ASSERT_NOT_NULL(ptr) \
do { \
auto *_tmp = (ptr); \
if((_tmp) == nullptr) { \
std::cerr << "ASSERT_NOT_NULL failed\n" \
<< " expression: \"" #ptr "\"\n" \
<< " file: " << __FILE__ << "\n" \
<< " line: " << __LINE__ << "\n" \
<< " function: " << __func__ << "\n"; \
assert((_tmp) != nullptr); \
} \
} while (0)
#endif
-10
View File
@@ -1,10 +0,0 @@
// Copyright David Abrahams 2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_WORKAROUND_DWA2002126_HPP
#define BOOST_WORKAROUND_DWA2002126_HPP
#include <boost/config/workaround.hpp>
#endif // BOOST_WORKAROUND_DWA2002126_HPP
-181
View File
@@ -1,181 +0,0 @@
// (C) Copyright John Maddock 2000.
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/libs/static_assert for documentation.
/*
Revision history:
02 August 2000
Initial version.
*/
#ifndef BOOST_STATIC_ASSERT_HPP
#define BOOST_STATIC_ASSERT_HPP
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#include <cstddef> //for std::size_t
#if defined(__GNUC__) && !defined(__GXX_EXPERIMENTAL_CXX0X__)
//
// This is horrible, but it seems to be the only we can shut up the
// "anonymous variadic macros were introduced in C99 [-Wvariadic-macros]"
// warning that get spewed out otherwise in non-C++11 mode.
//
#pragma GCC system_header
#endif
#ifndef BOOST_NO_CXX11_STATIC_ASSERT
# ifndef BOOST_NO_CXX11_VARIADIC_MACROS
# define BOOST_STATIC_ASSERT_MSG( ... ) static_assert(__VA_ARGS__)
# else
# define BOOST_STATIC_ASSERT_MSG( B, Msg ) static_assert( B, Msg )
# endif
#else
# define BOOST_STATIC_ASSERT_MSG( B, Msg ) BOOST_STATIC_ASSERT( B )
#endif
#ifdef BOOST_BORLANDC
//
// workaround for buggy integral-constant expression support:
#define BOOST_BUGGY_INTEGRAL_CONSTANT_EXPRESSIONS
#endif
#if defined(__GNUC__) && (__GNUC__ == 3) && ((__GNUC_MINOR__ == 3) || (__GNUC_MINOR__ == 4))
// gcc 3.3 and 3.4 don't produce good error messages with the default version:
# define BOOST_SA_GCC_WORKAROUND
#endif
//
// If the compiler issues warnings about old C style casts,
// then enable this:
//
#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4)))
# ifndef BOOST_NO_CXX11_VARIADIC_MACROS
# define BOOST_STATIC_ASSERT_BOOL_CAST( ... ) ((__VA_ARGS__) != 0)
# else
# define BOOST_STATIC_ASSERT_BOOL_CAST( x ) ((x) != 0)
# endif
#else
# ifndef BOOST_NO_CXX11_VARIADIC_MACROS
# define BOOST_STATIC_ASSERT_BOOL_CAST( ... ) (bool)(__VA_ARGS__)
# else
# define BOOST_STATIC_ASSERT_BOOL_CAST(x) (bool)(x)
# endif
#endif
#ifndef BOOST_NO_CXX11_STATIC_ASSERT
# ifndef BOOST_NO_CXX11_VARIADIC_MACROS
# define BOOST_STATIC_ASSERT( ... ) static_assert(__VA_ARGS__, #__VA_ARGS__)
# else
# define BOOST_STATIC_ASSERT( B ) static_assert(B, #B)
# endif
#else
namespace boost{
// HP aCC cannot deal with missing names for template value parameters
template <bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
// HP aCC cannot deal with missing names for template value parameters
template<std::size_t x> struct static_assert_test{};
}
//
// Implicit instantiation requires that all member declarations be
// instantiated, but that the definitions are *not* instantiated.
//
// It's not particularly clear how this applies to enum's or typedefs;
// both are described as declarations [7.1.3] and [7.2] in the standard,
// however some compilers use "delayed evaluation" of one or more of
// these when implicitly instantiating templates. We use typedef declarations
// by default, but try defining BOOST_USE_ENUM_STATIC_ASSERT if the enum
// version gets better results from your compiler...
//
// Implementation:
// Both of these versions rely on sizeof(incomplete_type) generating an error
// message containing the name of the incomplete type. We use
// "STATIC_ASSERTION_FAILURE" as the type name here to generate
// an eye catching error message. The result of the sizeof expression is either
// used as an enum initialiser, or as a template argument depending which version
// is in use...
// Note that the argument to the assert is explicitly cast to bool using old-
// style casts: too many compilers currently have problems with static_cast
// when used inside integral constant expressions.
//
#if !defined(BOOST_BUGGY_INTEGRAL_CONSTANT_EXPRESSIONS)
#if defined(BOOST_MSVC) && defined(BOOST_NO_CXX11_VARIADIC_MACROS)
#define BOOST_STATIC_ASSERT( B ) \
typedef ::boost::static_assert_test<\
sizeof(::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST ( B ) >)>\
BOOST_JOIN(boost_static_assert_typedef_, __COUNTER__)
#elif defined(BOOST_MSVC)
#define BOOST_STATIC_ASSERT(...) \
typedef ::boost::static_assert_test<\
sizeof(::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST (__VA_ARGS__) >)>\
BOOST_JOIN(boost_static_assert_typedef_, __COUNTER__)
#elif (defined(BOOST_INTEL_CXX_VERSION) || defined(BOOST_SA_GCC_WORKAROUND)) && defined(BOOST_NO_CXX11_VARIADIC_MACROS)
// agurt 15/sep/02: a special care is needed to force Intel C++ issue an error
// instead of warning in case of failure
# define BOOST_STATIC_ASSERT( B ) \
typedef char BOOST_JOIN(boost_static_assert_typedef_, __LINE__) \
[ ::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST( B ) >::value ]
#elif (defined(BOOST_INTEL_CXX_VERSION) || defined(BOOST_SA_GCC_WORKAROUND)) && !defined(BOOST_NO_CXX11_VARIADIC_MACROS)
// agurt 15/sep/02: a special care is needed to force Intel C++ issue an error
// instead of warning in case of failure
# define BOOST_STATIC_ASSERT(...) \
typedef char BOOST_JOIN(boost_static_assert_typedef_, __LINE__) \
[ ::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST( __VA_ARGS__ ) >::value ]
#elif defined(__sgi)
// special version for SGI MIPSpro compiler
#define BOOST_STATIC_ASSERT( B ) \
BOOST_STATIC_CONSTANT(bool, \
BOOST_JOIN(boost_static_assert_test_, __LINE__) = ( B )); \
typedef ::boost::static_assert_test<\
sizeof(::boost::STATIC_ASSERTION_FAILURE< \
BOOST_JOIN(boost_static_assert_test_, __LINE__) >)>\
BOOST_JOIN(boost_static_assert_typedef_, __LINE__)
#elif BOOST_WORKAROUND(__MWERKS__, <= 0x3003)
// special version for CodeWarrior <= 8.x
#define BOOST_STATIC_ASSERT( B ) \
BOOST_STATIC_CONSTANT(int, \
BOOST_JOIN(boost_static_assert_test_, __LINE__) = \
sizeof(::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST( B ) >) )
#else
// generic version
# ifndef BOOST_NO_CXX11_VARIADIC_MACROS
# define BOOST_STATIC_ASSERT( ... ) \
typedef ::boost::static_assert_test<\
sizeof(::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST( __VA_ARGS__ ) >)>\
BOOST_JOIN(boost_static_assert_typedef_, __LINE__) BOOST_ATTRIBUTE_UNUSED
# else
# define BOOST_STATIC_ASSERT( B ) \
typedef ::boost::static_assert_test<\
sizeof(::boost::STATIC_ASSERTION_FAILURE< BOOST_STATIC_ASSERT_BOOL_CAST( B ) >)>\
BOOST_JOIN(boost_static_assert_typedef_, __LINE__) BOOST_ATTRIBUTE_UNUSED
# endif
#endif
#else
// alternative enum based implementation:
# ifndef BOOST_NO_CXX11_VARIADIC_MACROS
# define BOOST_STATIC_ASSERT( ... ) \
enum { BOOST_JOIN(boost_static_assert_enum_, __LINE__) \
= sizeof(::boost::STATIC_ASSERTION_FAILURE< (bool)( __VA_ARGS__ ) >) }
# else
# define BOOST_STATIC_ASSERT(B) \
enum { BOOST_JOIN(boost_static_assert_enum_, __LINE__) \
= sizeof(::boost::STATIC_ASSERTION_FAILURE< (bool)( B ) >) }
# endif
#endif
#endif // defined(BOOST_NO_CXX11_STATIC_ASSERT)
#endif // BOOST_STATIC_ASSERT_HPP
-70
View File
@@ -1,70 +0,0 @@
/* Hash function characterization.
*
* Copyright 2022-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See https://www.boost.org/libs/unordered for library home page.
*/
#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP
#define BOOST_UNORDERED_HASH_TRAITS_HPP
#include <boost/unordered/detail/type_traits.hpp>
namespace boost{
namespace unordered{
namespace detail{
template<typename Hash,typename=void>
struct hash_is_avalanching_impl:std::false_type{};
template<typename IsAvalanching>
struct avalanching_value
{
static constexpr bool value=IsAvalanching::value;
};
/* may be explicitly marked as BOOST_DEPRECATED in the future */
template<> struct avalanching_value<void>
{
static constexpr bool value=true;
};
template<typename Hash>
struct hash_is_avalanching_impl<
Hash,
boost::unordered::detail::void_t<typename Hash::is_avalanching>
>:std::integral_constant<
bool,
avalanching_value<typename Hash::is_avalanching>::value
>{};
template<typename Hash>
struct hash_is_avalanching_impl<
Hash,
typename std::enable_if<((void)Hash::is_avalanching,true)>::type
>{}; /* Hash::is_avalanching is not a type: compile error downstream */
} /* namespace detail */
/* Each trait can be partially specialized by users for concrete hash functions
* when actual characterization differs from default.
*/
/* hash_is_avalanching<Hash>::value is:
* - false if Hash::is_avalanching is not present.
* - Hash::is_avalanching::value if this is present and constexpr-convertible
* to a bool.
* - true if Hash::is_avalanching is void (deprecated).
* - ill-formed otherwise.
*/
template<typename Hash>
struct hash_is_avalanching: detail::hash_is_avalanching_impl<Hash>::type{};
} /* namespace unordered */
} /* namespace boost */
#endif

Some files were not shown because too many files have changed in this diff Show More