Commit Graph
1566 Commits
Author SHA1 Message Date
Antonio SJ Musumeci bea31543a0 update fuse_lib_tmpfile() node management to match fuse_lib_create() 2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci 99835a0178 Document advisory locking limitation and update deprecated options
- Add documentation explaining that advisory file locking is not supported

- Add remember-nodes to deprecated options mapping

- Add comment explaining stat_crc32b fingerprint invalidation behavior

- Fix heading level for Host vs Container identity section
2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci 342a1efcfb Add QEMU binfmt setup and fix static installer
- Fix typo in static.installer: /pkg/ -> /pkgs/

- Add missing mkdir -p /build before makeself

- Mount pkgs directory via podman build -v so static.installer can access

  previously built static tarballs without relying on bind mounts relative

  to the build context

- Register qemu-user-static binfmt handlers for cross-compilation

- Make --git-repo optional when using --setup
2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci c587a127fb add missing <new> include for std::nothrow in fuse_session.cpp 2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci f1034a3ad6 Remove timed node removal 2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci 8117998205 Remove unused lock operation plumbing 2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci cc780bc521 libfuse: remove per-node lock bookkeeping 2026-05-06 20:49:46 -04:00
Antonio SJ Musumeci 14b7c8e607 Use stat fingerprint as cache validity sentinel
- Reserve a zero stat CRC to represent an invalid cache fingerprint.

- Include device, size, inode, mtime, and ctime data in the fingerprint.

- Recompute the fingerprint during auto-cache open before keeping cache.
2026-05-06 20:49:46 -04: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