mirror of https://github.com/trapexit/mergerfs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2885 lines
107 KiB
2885 lines
107 KiB
.\"t
|
|
.\" Automatically generated by Pandoc 1.19.2.4
|
|
.\"
|
|
.TH "mergerfs" "1" "2021\-02\-08" "mergerfs user manual" ""
|
|
.hy
|
|
.SH NAME
|
|
.PP
|
|
mergerfs \- a featureful union filesystem
|
|
.SH SYNOPSIS
|
|
.PP
|
|
mergerfs \-o<options> <branches> <mountpoint>
|
|
.SH DESCRIPTION
|
|
.PP
|
|
\f[B]mergerfs\f[] is a union filesystem geared towards simplifying
|
|
storage and management of files across numerous commodity storage
|
|
devices.
|
|
It is similar to \f[B]mhddfs\f[], \f[B]unionfs\f[], and \f[B]aufs\f[].
|
|
.SH FEATURES
|
|
.IP \[bu] 2
|
|
Runs in userspace (FUSE)
|
|
.IP \[bu] 2
|
|
Configurable behaviors / file placement
|
|
.IP \[bu] 2
|
|
Support for extended attributes (xattrs)
|
|
.IP \[bu] 2
|
|
Support for file attributes (chattr)
|
|
.IP \[bu] 2
|
|
Runtime configurable (via xattrs)
|
|
.IP \[bu] 2
|
|
Safe to run as root
|
|
.IP \[bu] 2
|
|
Opportunistic credential caching
|
|
.IP \[bu] 2
|
|
Works with heterogeneous filesystem types
|
|
.IP \[bu] 2
|
|
Handling of writes to full drives (transparently move file to drive with
|
|
capacity)
|
|
.IP \[bu] 2
|
|
Handles pool of read\-only and read/write drives
|
|
.IP \[bu] 2
|
|
Can turn read\-only files into symlinks to underlying file
|
|
.IP \[bu] 2
|
|
Hard link copy\-on\-write / CoW
|
|
.IP \[bu] 2
|
|
Support for POSIX ACLs
|
|
.SH HOW IT WORKS
|
|
.PP
|
|
mergerfs logically merges multiple paths together.
|
|
Think a union of sets.
|
|
The file/s or directory/s acted on or presented through mergerfs are
|
|
based on the policy chosen for that particular action.
|
|
Read more about policies below.
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
A\ \ \ \ \ \ \ \ \ +\ \ \ \ \ \ B\ \ \ \ \ \ \ \ =\ \ \ \ \ \ \ C
|
|
/disk1\ \ \ \ \ \ \ \ \ \ \ /disk2\ \ \ \ \ \ \ \ \ \ \ /merged
|
|
|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |
|
|
+\-\-\ /dir1\ \ \ \ \ \ \ \ +\-\-\ /dir1\ \ \ \ \ \ \ \ +\-\-\ /dir1
|
|
|\ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ |
|
|
|\ \ \ +\-\-\ file1\ \ \ \ |\ \ \ +\-\-\ file2\ \ \ \ |\ \ \ +\-\-\ file1
|
|
|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file3\ \ \ \ |\ \ \ +\-\-\ file2
|
|
+\-\-\ /dir2\ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file3
|
|
|\ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ /dir3\ \ \ \ \ \ \ \ |
|
|
|\ \ \ +\-\-\ file4\ \ \ \ \ \ \ \ |\ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ /dir2
|
|
|\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ file5\ \ \ |\ \ \ |
|
|
+\-\-\ file6\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file4
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ /dir3
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ |
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ +\-\-\ file5
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ |
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ +\-\-\ file6
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
mergerfs does \f[B]NOT\f[] support the copy\-on\-write (CoW) or whiteout
|
|
behaviors found in \f[B]aufs\f[] and \f[B]overlayfs\f[].
|
|
You can \f[B]not\f[] mount a read\-only filesystem and write to it.
|
|
However, mergerfs will ignore read\-only drives when creating new files
|
|
so you can mix read\-write and read\-only drives.
|
|
It also does \f[B]NOT\f[] split data across drives.
|
|
It is not RAID0 / striping.
|
|
It is simply a union of other filesystems.
|
|
.SH TERMINOLOGY
|
|
.IP \[bu] 2
|
|
branch: A base path used in the pool.
|
|
.IP \[bu] 2
|
|
pool: The mergerfs mount.
|
|
The union of the branches.
|
|
.IP \[bu] 2
|
|
relative path: The path in the pool relative to the branch and mount.
|
|
.IP \[bu] 2
|
|
function: A filesystem call (open, unlink, create, getattr, rmdir, etc.)
|
|
.IP \[bu] 2
|
|
category: A collection of functions based on basic behavior (action,
|
|
create, search).
|
|
.IP \[bu] 2
|
|
policy: The algorithm used to select a file when performing a function.
|
|
.IP \[bu] 2
|
|
path preservation: Aspect of some policies which includes checking the
|
|
path for which a file would be created.
|
|
.SH BASIC SETUP
|
|
.PP
|
|
If you don\[aq]t already know that you have a special use case then just
|
|
start with one of the following option sets.
|
|
.SS You need \f[C]mmap\f[] (used by rtorrent and many sqlite3 base
|
|
software)
|
|
.PP
|
|
\f[C]allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs\f[]
|
|
.SS You don\[aq]t need \f[C]mmap\f[]
|
|
.PP
|
|
\f[C]allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs\f[]
|
|
.PP
|
|
See the mergerfs wiki for real world
|
|
deployments (https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments)
|
|
for comparisons / ideas.
|
|
.SH OPTIONS
|
|
.PP
|
|
These options are the same regardless you use them with the
|
|
\f[C]mergerfs\f[] commandline program, used in fstab, or in a config
|
|
file.
|
|
.SS mount options
|
|
.IP \[bu] 2
|
|
\f[B]config\f[]: Path to a config file.
|
|
Same arguments as below in key=val / ini style format.
|
|
.IP \[bu] 2
|
|
\f[B]branches\f[]: Colon delimited list of branches.
|
|
.IP \[bu] 2
|
|
\f[B]allow_other\f[]: A libfuse option which allows users besides the
|
|
one which ran mergerfs to see the filesystem.
|
|
This is required for most use\-cases.
|
|
.IP \[bu] 2
|
|
\f[B]minfreespace=SIZE\f[]: The minimum space value used for creation
|
|
policies.
|
|
Can be overridden by branch specific option.
|
|
Understands \[aq]K\[aq], \[aq]M\[aq], and \[aq]G\[aq] to represent
|
|
kilobyte, megabyte, and gigabyte respectively.
|
|
(default: 4G)
|
|
.IP \[bu] 2
|
|
\f[B]moveonenospc=BOOL|POLICY\f[]: When enabled if a \f[B]write\f[]
|
|
fails with \f[B]ENOSPC\f[] (no space left on device) or \f[B]EDQUOT\f[]
|
|
(disk quota exceeded) the policy selected will run to find a new
|
|
location for the file.
|
|
An attempt to move the file to that branch will occur (keeping all
|
|
metadata possible) and if successful the original is unlinked and the
|
|
write retried.
|
|
(default: false, true = mfs)
|
|
.IP \[bu] 2
|
|
\f[B]use_ino\f[]: Causes mergerfs to supply file/directory inodes rather
|
|
than libfuse.
|
|
While not a default it is recommended it be enabled so that linked files
|
|
share the same inode value.
|
|
.IP \[bu] 2
|
|
\f[B]inodecalc=passthrough|path\-hash|devino\-hash|hybrid\-hash\f[]:
|
|
Selects the inode calculation algorithm.
|
|
(default: hybrid\-hash)
|
|
.IP \[bu] 2
|
|
\f[B]dropcacheonclose=BOOL\f[]: When a file is requested to be closed
|
|
call \f[C]posix_fadvise\f[] on it first to instruct the kernel that we
|
|
no longer need the data and it can drop its cache.
|
|
Recommended when \f[B]cache.files=partial|full|auto\-full\f[] to limit
|
|
double caching.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]symlinkify=BOOL\f[]: When enabled and a file is not writable and
|
|
its mtime or ctime is older than \f[B]symlinkify_timeout\f[] files will
|
|
be reported as symlinks to the original files.
|
|
Please read more below before using.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]symlinkify_timeout=UINT\f[]: Time to wait, in seconds, to activate
|
|
the \f[B]symlinkify\f[] behavior.
|
|
(default: 3600)
|
|
.IP \[bu] 2
|
|
\f[B]nullrw=BOOL\f[]: Turns reads and writes into no\-ops.
|
|
The request will succeed but do nothing.
|
|
Useful for benchmarking mergerfs.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]ignorepponrename=BOOL\f[]: Ignore path preserving on rename.
|
|
Typically rename and link act differently depending on the policy of
|
|
\f[C]create\f[] (read below).
|
|
Enabling this will cause rename and link to always use the non\-path
|
|
preserving behavior.
|
|
This means files, when renamed or linked, will stay on the same drive.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]security_capability=BOOL\f[]: If false return ENOATTR when xattr
|
|
security.capability is queried.
|
|
(default: true)
|
|
.IP \[bu] 2
|
|
\f[B]xattr=passthrough|noattr|nosys\f[]: Runtime control of xattrs.
|
|
Default is to passthrough xattr requests.
|
|
\[aq]noattr\[aq] will short circuit as if nothing exists.
|
|
\[aq]nosys\[aq] will respond with ENOSYS as if xattrs are not supported
|
|
or disabled.
|
|
(default: passthrough)
|
|
.IP \[bu] 2
|
|
\f[B]link_cow=BOOL\f[]: When enabled if a regular file is opened which
|
|
has a link count > 1 it will copy the file to a temporary file and
|
|
rename over the original.
|
|
Breaking the link and providing a basic copy\-on\-write function similar
|
|
to cow\-shell.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]statfs=base|full\f[]: Controls how statfs works.
|
|
\[aq]base\[aq] means it will always use all branches in statfs
|
|
calculations.
|
|
\[aq]full\[aq] is in effect path preserving and only includes drives
|
|
where the path exists.
|
|
(default: base)
|
|
.IP \[bu] 2
|
|
\f[B]statfs_ignore=none|ro|nc\f[]: \[aq]ro\[aq] will cause statfs
|
|
calculations to ignore available space for branches mounted or tagged as
|
|
\[aq]read\-only\[aq] or \[aq]no create\[aq].
|
|
\[aq]nc\[aq] will ignore available space for branches tagged as \[aq]no
|
|
create\[aq].
|
|
(default: none)
|
|
.IP \[bu] 2
|
|
\f[B]nfsopenhack=off|git|all\f[]: A workaround for exporting mergerfs
|
|
over NFS where there are issues with creating files for write while
|
|
setting the mode to read\-only.
|
|
(default: off)
|
|
.IP \[bu] 2
|
|
\f[B]follow\-symlinks=never|directory|regular|all\f[]: Turns symlinks
|
|
into what they point to.
|
|
(default: never)
|
|
.IP \[bu] 2
|
|
\f[B]link\-exdev=passthrough|rel\-symlink|abs\-base\-symlink|abs\-pool\-symlink\f[]:
|
|
When a link fails with EXDEV optionally create a symlink to the file
|
|
instead.
|
|
.IP \[bu] 2
|
|
\f[B]rename\-exdev=passthrough|rel\-symlink|abs\-symlink\f[]: When a
|
|
rename fails with EXDEV optionally move the file to a special directory
|
|
and symlink to it.
|
|
.IP \[bu] 2
|
|
\f[B]posix_acl=BOOL\f[]: Enable POSIX ACL support (if supported by
|
|
kernel and underlying filesystem).
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]async_read=BOOL\f[]: Perform reads asynchronously.
|
|
If disabled or unavailable the kernel will ensure there is at most one
|
|
pending read request per file handle and will attempt to order requests
|
|
by offset.
|
|
(default: true)
|
|
.IP \[bu] 2
|
|
\f[B]fuse_msg_size=UINT\f[]: Set the max number of pages per FUSE
|
|
message.
|
|
Only available on Linux >= 4.20 and ignored otherwise.
|
|
(min: 1; max: 256; default: 256)
|
|
.IP \[bu] 2
|
|
\f[B]threads=INT\f[]: Number of threads to use in multithreaded mode.
|
|
When set to zero it will attempt to discover and use the number of
|
|
logical cores.
|
|
If the lookup fails it will fall back to using 4.
|
|
If the thread count is set negative it will look up the number of cores
|
|
then divide by the absolute value.
|
|
ie.
|
|
threads=\-2 on an 8 core machine will result in 8 / 2 = 4 threads.
|
|
There will always be at least 1 thread.
|
|
NOTE: higher number of threads increases parallelism but usually
|
|
decreases throughput.
|
|
(default: 0)
|
|
.IP \[bu] 2
|
|
\f[B]fsname=STR\f[]: Sets the name of the filesystem as seen in
|
|
\f[B]mount\f[], \f[B]df\f[], etc.
|
|
Defaults to a list of the source paths concatenated together with the
|
|
longest common prefix removed.
|
|
.IP \[bu] 2
|
|
\f[B]func.FUNC=POLICY\f[]: Sets the specific FUSE function\[aq]s policy.
|
|
See below for the list of value types.
|
|
Example: \f[B]func.getattr=newest\f[]
|
|
.IP \[bu] 2
|
|
\f[B]category.action=POLICY\f[]: Sets policy of all FUSE functions in
|
|
the action category.
|
|
(default: epall)
|
|
.IP \[bu] 2
|
|
\f[B]category.create=POLICY\f[]: Sets policy of all FUSE functions in
|
|
the create category.
|
|
(default: epmfs)
|
|
.IP \[bu] 2
|
|
\f[B]category.search=POLICY\f[]: Sets policy of all FUSE functions in
|
|
the search category.
|
|
(default: ff)
|
|
.IP \[bu] 2
|
|
\f[B]cache.open=UINT\f[]: \[aq]open\[aq] policy cache timeout in
|
|
seconds.
|
|
(default: 0)
|
|
.IP \[bu] 2
|
|
\f[B]cache.statfs=UINT\f[]: \[aq]statfs\[aq] cache timeout in seconds.
|
|
(default: 0)
|
|
.IP \[bu] 2
|
|
\f[B]cache.attr=UINT\f[]: File attribute cache timeout in seconds.
|
|
(default: 1)
|
|
.IP \[bu] 2
|
|
\f[B]cache.entry=UINT\f[]: File name lookup cache timeout in seconds.
|
|
(default: 1)
|
|
.IP \[bu] 2
|
|
\f[B]cache.negative_entry=UINT\f[]: Negative file name lookup cache
|
|
timeout in seconds.
|
|
(default: 0)
|
|
.IP \[bu] 2
|
|
\f[B]cache.files=libfuse|off|partial|full|auto\-full\f[]: File page
|
|
caching mode (default: libfuse)
|
|
.IP \[bu] 2
|
|
\f[B]cache.writeback=BOOL\f[]: Enable kernel writeback caching (default:
|
|
false)
|
|
.IP \[bu] 2
|
|
\f[B]cache.symlinks=BOOL\f[]: Cache symlinks (if supported by kernel)
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]cache.readdir=BOOL\f[]: Cache readdir (if supported by kernel)
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]direct_io\f[]: deprecated \- Bypass page cache.
|
|
Use \f[C]cache.files=off\f[] instead.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]kernel_cache\f[]: deprecated \- Do not invalidate data cache on
|
|
file open.
|
|
Use \f[C]cache.files=full\f[] instead.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]auto_cache\f[]: deprecated \- Invalidate data cache if file mtime
|
|
or size change.
|
|
Use \f[C]cache.files=auto\-full\f[] instead.
|
|
(default: false)
|
|
.IP \[bu] 2
|
|
\f[B]async_read\f[]: deprecated \- Perform reads asynchronously.
|
|
Use \f[C]async_read=true\f[] instead.
|
|
.IP \[bu] 2
|
|
\f[B]sync_read\f[]: deprecated \- Perform reads synchronously.
|
|
Use \f[C]async_read=false\f[] instead.
|
|
.PP
|
|
\f[B]NOTE:\f[] Options are evaluated in the order listed so if the
|
|
options are \f[B]func.rmdir=rand,category.action=ff\f[] the
|
|
\f[B]action\f[] category setting will override the \f[B]rmdir\f[]
|
|
setting.
|
|
.SS Value Types
|
|
.IP \[bu] 2
|
|
BOOL = \[aq]true\[aq] | \[aq]false\[aq]
|
|
.IP \[bu] 2
|
|
INT = [MIN_INT,MAX_INT]
|
|
.IP \[bu] 2
|
|
UINT = [0,MAX_INT]
|
|
.IP \[bu] 2
|
|
SIZE = \[aq]NNM\[aq]; NN = INT, M = \[aq]K\[aq] | \[aq]M\[aq] |
|
|
\[aq]G\[aq] | \[aq]T\[aq]
|
|
.IP \[bu] 2
|
|
STR = string
|
|
.IP \[bu] 2
|
|
FUNC = filesystem function
|
|
.IP \[bu] 2
|
|
CATEGORY = function category
|
|
.IP \[bu] 2
|
|
POLICY = mergerfs function policy
|
|
.SS branches
|
|
.PP
|
|
The \[aq]branches\[aq] argument is a colon (\[aq]:\[aq]) delimited list
|
|
of paths to be pooled together.
|
|
It does not matter if the paths are on the same or different drives nor
|
|
does it matter the filesystem (within reason).
|
|
Used and available space will not be duplicated for paths on the same
|
|
device and any features which aren\[aq]t supported by the underlying
|
|
filesystem (such as file attributes or extended attributes) will return
|
|
the appropriate errors.
|
|
.PP
|
|
Branches currently have two options which can be set.
|
|
A type which impacts whether or not the branch is included in a policy
|
|
calculation and a individual minfreespace value.
|
|
The values are set by prepending an \f[C]=\f[] at the end of a branch
|
|
designation and using commas as delimiters.
|
|
Example: /mnt/drive=RW,1234
|
|
.SS branch type
|
|
.IP \[bu] 2
|
|
RW: (read/write) \- Default behavior.
|
|
Will be eligible in all policy categories.
|
|
.IP \[bu] 2
|
|
RO: (read\-only) \- Will be excluded from \f[C]create\f[] and
|
|
\f[C]action\f[] policies.
|
|
Same as a read\-only mounted filesystem would be (though faster to
|
|
process).
|
|
.IP \[bu] 2
|
|
NC: (no\-create) \- Will be excluded from \f[C]create\f[] policies.
|
|
You can\[aq]t create on that branch but you can change or delete.
|
|
.SS minfreespace
|
|
.PP
|
|
Same purpose as the global option but specific to the branch.
|
|
If not set the global value is used.
|
|
.SS globbing
|
|
.PP
|
|
To make it easier to include multiple branches mergerfs supports
|
|
globbing (http://linux.die.net/man/7/glob).
|
|
\f[B]The globbing tokens MUST be escaped when using via the shell else
|
|
the shell itself will apply the glob itself.\f[]
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
#\ mergerfs\ \-o\ allow_other,use_ino\ /mnt/disk\\*:/mnt/cdrom\ /media/drives
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
The above line will use all mount points in /mnt prefixed with
|
|
\f[B]disk\f[] and the \f[B]cdrom\f[].
|
|
.PP
|
|
To have the pool mounted at boot or otherwise accessible from related
|
|
tools use \f[B]/etc/fstab\f[].
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
#\ <file\ system>\ \ \ \ \ \ \ \ <mount\ point>\ \ <type>\ \ \ \ \ \ \ \ \ <options>\ \ \ \ \ \ \ \ \ \ \ \ \ <dump>\ \ <pass>
|
|
/mnt/disk*:/mnt/cdrom\ \ /mnt/pool\ \ \ \ \ \ fuse.mergerfs\ \ allow_other,use_ino\ \ \ 0\ \ \ \ \ \ \ 0
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
\f[B]NOTE:\f[] the globbing is done at mount or when updated using the
|
|
runtime API.
|
|
If a new directory is added matching the glob after the fact it will not
|
|
be automatically included.
|
|
.PP
|
|
\f[B]NOTE:\f[] for mounting via \f[B]fstab\f[] to work you must have
|
|
\f[B]mount.fuse\f[] installed.
|
|
For Ubuntu/Debian it is included in the \f[B]fuse\f[] package.
|
|
.SS inodecalc
|
|
.PP
|
|
Inodes (st_ino) are unique identifiers within a filesystem.
|
|
Each mounted filesystem has device ID (st_dev) as well and together they
|
|
can uniquely identify a file on the whole of the system.
|
|
Entries on the same device with the same inode are in fact references to
|
|
the same underlying file.
|
|
It is a many to one relationship between names and an inode.
|
|
Directories, however, do not have multiple links on most systems due to
|
|
the complexity they add.
|
|
.PP
|
|
FUSE allows the server (mergerfs) to set inode values but not device
|
|
IDs.
|
|
Creating an inode value is somewhat complex in mergerfs\[aq] case as
|
|
files aren\[aq]t really in its control.
|
|
If a policy changes what directory or file is to be selected or
|
|
something changes out of band it becomes unclear what value should be
|
|
used.
|
|
Most software does not to care what the values are but those that do
|
|
often break if a value changes unexpectedly.
|
|
The tool \f[C]find\f[] will abort a directory walk if it sees a
|
|
directory inode change.
|
|
NFS will return stale handle errors if the inode changes out of band.
|
|
File dedup tools will usually leverage device ids and inodes as a
|
|
shortcut in searching for duplicate files and would resort to full file
|
|
comparisons should it find different inode values.
|
|
.PP
|
|
mergerfs offers multiple ways to calculate the inode in hopes of
|
|
covering different usecases.
|
|
.IP \[bu] 2
|
|
passthrough: Passes through the underlying inode value.
|
|
Mostly intended for testing as using this does not address any of the
|
|
problems mentioned above and could confuse file deduplication software
|
|
as inodes from different filesystems can be the same.
|
|
.IP \[bu] 2
|
|
path\-hash: Hashes the relative path of the entry in question.
|
|
The underlying file\[aq]s values are completely ignored.
|
|
This means the inode value will always be the same for that file path.
|
|
This is useful when using NFS and you make changes out of band such as
|
|
copy data between branches.
|
|
This also means that entries that do point to the same file will not be
|
|
recognizable via inodes.
|
|
That \f[B]does not\f[] mean hard links don\[aq]t work.
|
|
They will.
|
|
.IP \[bu] 2
|
|
path\-hash32: 32bit version of path\-hash.
|
|
.IP \[bu] 2
|
|
devino\-hash: Hashes the device id and inode of the underlying entry.
|
|
This won\[aq]t prevent issues with NFS should the policy pick a
|
|
different file or files move out of band but will present the same inode
|
|
for underlying files that do too.
|
|
.IP \[bu] 2
|
|
devino\-hash32: 32bit version of devino\-hash.
|
|
.IP \[bu] 2
|
|
hybrid\-hash: Performs \f[C]path\-hash\f[] on directories and
|
|
\f[C]devino\-hash\f[] on other file types.
|
|
Since directories can\[aq]t have hard links the static value won\[aq]t
|
|
make a difference and the files will get values useful for finding
|
|
duplicates.
|
|
Probably the best to use if not using NFS.
|
|
As such it is the default.
|
|
.IP \[bu] 2
|
|
hybrid\-hash32: 32bit version of hybrid\-hash.
|
|
.PP
|
|
32bit versions are provided as there is some software which does not
|
|
handle 64bit inodes well.
|
|
.PP
|
|
While there is a risk of hash collision in tests of a couple million
|
|
entries there were zero collisions.
|
|
Unlike a typical filesystem FUSE filesystems can reuse inodes and not
|
|
refer to the same entry.
|
|
The internal identifier used to reference a file in FUSE is different
|
|
from the inode value presented.
|
|
The former is the \f[C]nodeid\f[] and is actually a tuple of 2 64bit
|
|
values: \f[C]nodeid\f[] and \f[C]generation\f[].
|
|
This tuple is not client facing.
|
|
The inode that is presented to the client is passed through the kernel
|
|
uninterpreted.
|
|
.PP
|
|
From FUSE docs regarding \f[C]use_ino\f[]:
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
Honor\ the\ st_ino\ field\ in\ the\ functions\ getattr()\ and
|
|
fill_dir().\ This\ value\ is\ used\ to\ fill\ in\ the\ st_ino\ field
|
|
in\ the\ stat(2),\ lstat(2),\ fstat(2)\ functions\ and\ the\ d_ino
|
|
field\ in\ the\ readdir(2)\ function.\ The\ filesystem\ does\ not
|
|
have\ to\ guarantee\ uniqueness,\ however\ some\ applications
|
|
rely\ on\ this\ value\ being\ unique\ for\ the\ whole\ filesystem.
|
|
Note\ that\ this\ does\ *not*\ affect\ the\ inode\ that\ libfuse
|
|
and\ the\ kernel\ use\ internally\ (also\ called\ the\ "nodeid").
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
In the future the \f[C]use_ino\f[] option will probably be removed as
|
|
this feature should replace the original libfuse inode calculation
|
|
strategy.
|
|
Currently you still need to use \f[C]use_ino\f[] in order to enable
|
|
\f[C]inodecalc\f[].
|
|
.SS fuse_msg_size
|
|
.PP
|
|
FUSE applications communicate with the kernel over a special character
|
|
device: \f[C]/dev/fuse\f[].
|
|
A large portion of the overhead associated with FUSE is the cost of
|
|
going back and forth from user space and kernel space over that device.
|
|
Generally speaking the fewer trips needed the better the performance
|
|
will be.
|
|
Reducing the number of trips can be done a number of ways.
|
|
Kernel level caching and increasing message sizes being two significant
|
|
ones.
|
|
When it comes to reads and writes if the message size is doubled the
|
|
number of trips are approximately halved.
|
|
.PP
|
|
In Linux 4.20 a new feature was added allowing the negotiation of the
|
|
max message size.
|
|
Since the size is in multiples of
|
|
pages (https://en.wikipedia.org/wiki/Page_(computer_memory)) the feature
|
|
is called \f[C]max_pages\f[].
|
|
There is a maximum \f[C]max_pages\f[] value of 256 (1MiB) and minimum of
|
|
1 (4KiB).
|
|
The default used by Linux >=4.20, and hardcoded value used before 4.20,
|
|
is 32 (128KiB).
|
|
In mergerfs its referred to as \f[C]fuse_msg_size\f[] to make it clear
|
|
what it impacts and provide some abstraction.
|
|
.PP
|
|
Since there should be no downsides to increasing \f[C]fuse_msg_size\f[]
|
|
/ \f[C]max_pages\f[], outside a minor bump in RAM usage due to larger
|
|
message buffers, mergerfs defaults the value to 256.
|
|
On kernels before 4.20 the value has no effect.
|
|
The reason the value is configurable is to enable experimentation and
|
|
benchmarking.
|
|
See the BENCHMARKING section for examples.
|
|
.SS follow\-symlinks
|
|
.PP
|
|
This feature, when enabled, will cause symlinks to be interpreted by
|
|
mergerfs as their target (depending on the mode).
|
|
.PP
|
|
When there is a getattr/stat request for a file mergerfs will check if
|
|
the file is a symlink and depending on the \f[C]follow\-symlinks\f[]
|
|
setting will replace the information about the symlink with that of that
|
|
which it points to.
|
|
.PP
|
|
When unlink\[aq]ing or rmdir\[aq]ing the followed symlink it will remove
|
|
the symlink itself and not that which it points to.
|
|
.IP \[bu] 2
|
|
never: Behave as normal.
|
|
Symlinks are treated as such.
|
|
.IP \[bu] 2
|
|
directory: Resolve symlinks only which point to directories.
|
|
.IP \[bu] 2
|
|
regular: Resolve symlinks only which point to regular files.
|
|
.IP \[bu] 2
|
|
all: Resolve all symlinks to that which they point to.
|
|
.PP
|
|
Symlinks which do not point to anything are left as is.
|
|
.PP
|
|
WARNING: This feature works but there might be edge cases yet found.
|
|
If you find any odd behaviors please file a ticket on
|
|
github (https://github.com/trapexit/mergerfs/issues).
|
|
.SS link\-exdev
|
|
.PP
|
|
If using path preservation and a \f[C]link\f[] fails with EXDEV make a
|
|
call to \f[C]symlink\f[] where the \f[C]target\f[] is the
|
|
\f[C]oldlink\f[] and the \f[C]linkpath\f[] is the \f[C]newpath\f[].
|
|
The \f[C]target\f[] value is determined by the value of
|
|
\f[C]link\-exdev\f[].
|
|
.IP \[bu] 2
|
|
passthrough: Return EXDEV as normal.
|
|
.IP \[bu] 2
|
|
rel\-symlink: A relative path from the \f[C]newpath\f[].
|
|
.IP \[bu] 2
|
|
abs\-base\-symlink: A absolute value using the underlying branch.
|
|
.IP \[bu] 2
|
|
abs\-pool\-symlink: A absolute value using the mergerfs mount point.
|
|
.PP
|
|
NOTE: It is possible that some applications check the file they link.
|
|
In those cases it is possible it will error or complain.
|
|
.SS rename\-exdev
|
|
.PP
|
|
If using path preservation and a \f[C]rename\f[] fails with EXDEV:
|
|
.IP "1." 3
|
|
Move file from \f[B]/branch/a/b/c\f[] to
|
|
\f[B]/branch/.mergerfs_rename_exdev/a/b/c\f[].
|
|
.IP "2." 3
|
|
symlink the rename\[aq]s \f[C]newpath\f[] to the moved file.
|
|
.PP
|
|
The \f[C]target\f[] value is determined by the value of
|
|
\f[C]rename\-exdev\f[].
|
|
.IP \[bu] 2
|
|
passthrough: Return EXDEV as normal.
|
|
.IP \[bu] 2
|
|
rel\-symlink: A relative path from the \f[C]newpath\f[].
|
|
.IP \[bu] 2
|
|
abs\-symlink: A absolute value using the mergerfs mount point.
|
|
.PP
|
|
NOTE: It is possible that some applications check the file they rename.
|
|
In those cases it is possible it will error or complain.
|
|
.PP
|
|
NOTE: The reason \f[C]abs\-symlink\f[] is not split into two like
|
|
\f[C]link\-exdev\f[] is due to the complexities in managing absolute
|
|
base symlinks when multiple \f[C]oldpaths\f[] exist.
|
|
.SS symlinkify
|
|
.PP
|
|
Due to the levels of indirection introduced by mergerfs and the
|
|
underlying technology FUSE there can be varying levels of performance
|
|
degradation.
|
|
This feature will turn non\-directories which are not writable into
|
|
symlinks to the original file found by the \f[C]readlink\f[] policy
|
|
after the mtime and ctime are older than the timeout.
|
|
.PP
|
|
\f[B]WARNING:\f[] The current implementation has a known issue in which
|
|
if the file is open and being used when the file is converted to a
|
|
symlink then the application which has that file open will receive an
|
|
error when using it.
|
|
This is unlikely to occur in practice but is something to keep in mind.
|
|
.PP
|
|
\f[B]WARNING:\f[] Some backup solutions, such as CrashPlan, do not
|
|
backup the target of a symlink.
|
|
If using this feature it will be necessary to point any backup software
|
|
to the original drives or configure the software to follow symlinks if
|
|
such an option is available.
|
|
Alternatively create two mounts.
|
|
One for backup and one for general consumption.
|
|
.SS nullrw
|
|
.PP
|
|
Due to how FUSE works there is an overhead to all requests made to a
|
|
FUSE filesystem that wouldn\[aq]t exist for an in kernel one.
|
|
Meaning that even a simple passthrough will have some slowdown.
|
|
However, generally the overhead is minimal in comparison to the cost of
|
|
the underlying I/O.
|
|
By disabling the underlying I/O we can test the theoretical performance
|
|
boundaries.
|
|
.PP
|
|
By enabling \f[C]nullrw\f[] mergerfs will work as it always does
|
|
\f[B]except\f[] that all reads and writes will be no\-ops.
|
|
A write will succeed (the size of the write will be returned as if it
|
|
were successful) but mergerfs does nothing with the data it was given.
|
|
Similarly a read will return the size requested but won\[aq]t touch the
|
|
buffer.
|
|
.PP
|
|
See the BENCHMARKING section for suggestions on how to test.
|
|
.SS xattr
|
|
.PP
|
|
Runtime extended attribute support can be managed via the \f[C]xattr\f[]
|
|
option.
|
|
By default it will passthrough any xattr calls.
|
|
Given xattr support is rarely used and can have significant performance
|
|
implications mergerfs allows it to be disabled at runtime.
|
|
The performance problems mostly comes when file caching is enabled.
|
|
The kernel will send a \f[C]getxattr\f[] for
|
|
\f[C]security.capability\f[] \f[I]before every single write\f[].
|
|
It doesn\[aq]t cache the responses to any \f[C]getxattr\f[].
|
|
This might be addressed in the future but for now mergerfs can really
|
|
only offer the following workarounds.
|
|
.PP
|
|
\f[C]noattr\f[] will cause mergerfs to short circuit all xattr calls and
|
|
return ENOATTR where appropriate.
|
|
mergerfs still gets all the requests but they will not be forwarded on
|
|
to the underlying filesystems.
|
|
The runtime control will still function in this mode.
|
|
.PP
|
|
\f[C]nosys\f[] will cause mergerfs to return ENOSYS for any xattr call.
|
|
The difference with \f[C]noattr\f[] is that the kernel will cache this
|
|
fact and itself short circuit future calls.
|
|
This is more efficient than \f[C]noattr\f[] but will cause mergerfs\[aq]
|
|
runtime control via the hidden file to stop working.
|
|
.SS nfsopenhack
|
|
.PP
|
|
NFS is not fully POSIX compliant and historically certain behaviors,
|
|
such as opening files with O_EXCL, are not or not well supported.
|
|
When mergerfs (or any FUSE filesystem) is exported over NFS some of
|
|
these issues come up due to how NFS and FUSE interact.
|
|
.PP
|
|
This hack addresses the issue where the creation of a file with a
|
|
read\-only mode but with a read/write or write only flag.
|
|
Normally this is perfectly valid but NFS chops the one open call into
|
|
multiple calls.
|
|
Exactly how it is translated depends on the configuration and versions
|
|
of the NFS server and clients but it results in a permission error
|
|
because a normal user is not allowed to open a read\-only file as
|
|
writable.
|
|
.PP
|
|
Even though it\[aq]s a more niche situation this hack breaks normal
|
|
security and behavior and as such is \f[C]off\f[] by default.
|
|
If set to \f[C]git\f[] it will only perform the hack when the path in
|
|
question includes \f[C]/.git/\f[].
|
|
\f[C]all\f[] will result it it applying anytime a readonly file which is
|
|
empty is opened for writing.
|
|
.SH FUNCTIONS, CATEGORIES and POLICIES
|
|
.PP
|
|
The POSIX filesystem API is made up of a number of functions.
|
|
\f[B]creat\f[], \f[B]stat\f[], \f[B]chown\f[], etc.
|
|
For ease of configuration in mergerfs most of the core functions are
|
|
grouped into 3 categories: \f[B]action\f[], \f[B]create\f[], and
|
|
\f[B]search\f[].
|
|
These functions and categories can be assigned a policy which dictates
|
|
which branch is chosen when performing that function.
|
|
.PP
|
|
Some functions, listed in the category \f[C]N/A\f[] below, can not be
|
|
assigned the normal policies.
|
|
These functions work with file handles, rather than file paths, which
|
|
were created by \f[C]open\f[] or \f[C]create\f[].
|
|
That said many times the current FUSE kernel driver will not always
|
|
provide the file handle when a client calls \f[C]fgetattr\f[],
|
|
\f[C]fchown\f[], \f[C]fchmod\f[], \f[C]futimens\f[], \f[C]ftruncate\f[],
|
|
etc.
|
|
This means it will call the regular, path based, versions.
|
|
\f[C]readdir\f[] has no real need for a policy given the purpose is
|
|
merely to return a list of entries in a directory.
|
|
\f[C]statfs\f[]\[aq]s behavior can be modified via other options.
|
|
.PP
|
|
When using policies which are based on a branch\[aq]s available space
|
|
the base path provided is used.
|
|
Not the full path to the file in question.
|
|
Meaning that mounts in the branch won\[aq]t be considered in the space
|
|
calculations.
|
|
The reason is that it doesn\[aq]t really work for non\-path preserving
|
|
policies and can lead to non\-obvious behaviors.
|
|
.PP
|
|
NOTE: While any policy can be assigned to a function or category though
|
|
some may not be very useful in practice.
|
|
For instance: \f[B]rand\f[] (random) may be useful for file creation
|
|
(create) but could lead to very odd behavior if used for \f[C]chmod\f[]
|
|
if there were more than one copy of the file.
|
|
.SS Functions and their Category classifications
|
|
.PP
|
|
.TS
|
|
tab(@);
|
|
lw(7.9n) lw(62.1n).
|
|
T{
|
|
Category
|
|
T}@T{
|
|
FUSE Functions
|
|
T}
|
|
_
|
|
T{
|
|
action
|
|
T}@T{
|
|
chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate,
|
|
unlink, utimens
|
|
T}
|
|
T{
|
|
create
|
|
T}@T{
|
|
create, mkdir, mknod, symlink
|
|
T}
|
|
T{
|
|
search
|
|
T}@T{
|
|
access, getattr, getxattr, ioctl (directories), listxattr, open,
|
|
readlink
|
|
T}
|
|
T{
|
|
N/A
|
|
T}@T{
|
|
fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl
|
|
(files), read, readdir, release, statfs, write, copy_file_range
|
|
T}
|
|
.TE
|
|
.PP
|
|
In cases where something may be searched for (such as a path to clone)
|
|
\f[B]getattr\f[] will usually be used.
|
|
.SS Policies
|
|
.PP
|
|
A policy is the algorithm used to choose a branch or branches for a
|
|
function to work on.
|
|
Think of them as ways to filter and sort branches.
|
|
.PP
|
|
Any function in the \f[C]create\f[] category will clone the relative
|
|
path if needed.
|
|
Some other functions (\f[C]rename\f[],\f[C]link\f[],\f[C]ioctl\f[]) have
|
|
special requirements or behaviors which you can read more about below.
|
|
.SS Filtering
|
|
.PP
|
|
Policies basically search branches and create a list of files / paths
|
|
for functions to work on.
|
|
The policy is responsible for filtering and sorting the branches.
|
|
Filters include \f[B]minfreespace\f[], whether or not a branch is
|
|
mounted read\-only, and the branch tagging (RO,NC,RW).
|
|
These filters are applied across all policies unless otherwise noted.
|
|
.IP \[bu] 2
|
|
No \f[B]search\f[] function policies filter.
|
|
.IP \[bu] 2
|
|
All \f[B]action\f[] function policies filter out branches which are
|
|
mounted \f[B]read\-only\f[] or tagged as \f[B]RO (read\-only)\f[].
|
|
.IP \[bu] 2
|
|
All \f[B]create\f[] function policies filter out branches which are
|
|
mounted \f[B]read\-only\f[], tagged \f[B]RO (read\-only)\f[] or \f[B]NC
|
|
(no create)\f[], or has available space less than \f[C]minfreespace\f[].
|
|
.PP
|
|
Policies may have their own additional filtering such as those that
|
|
require existing paths to be present.
|
|
.PP
|
|
If all branches are filtered an error will be returned.
|
|
Typically \f[B]EROFS\f[] (read\-only filesystem) or \f[B]ENOSPC\f[] (no
|
|
space left on device) depending on the most recent reason for filtering
|
|
a branch.
|
|
\f[B]ENOENT\f[] will be returned if no elegible branch is found.
|
|
.SS Path Preservation
|
|
.PP
|
|
Policies, as described below, are of two basic types.
|
|
\f[C]path\ preserving\f[] and \f[C]non\-path\ preserving\f[].
|
|
.PP
|
|
All policies which start with \f[C]ep\f[] (\f[B]epff\f[],
|
|
\f[B]eplfs\f[], \f[B]eplus\f[], \f[B]epmfs\f[], \f[B]eprand\f[]) are
|
|
\f[C]path\ preserving\f[].
|
|
\f[C]ep\f[] stands for \f[C]existing\ path\f[].
|
|
.PP
|
|
A path preserving policy will only consider drives where the relative
|
|
path being accessed already exists.
|
|
.PP
|
|
When using non\-path preserving policies paths will be cloned to target
|
|
drives as necessary.
|
|
.PP
|
|
With the \f[C]msp\f[] or \f[C]most\ shared\ path\f[] policies they are
|
|
defined as \f[C]path\ preserving\f[] for the purpose of controlling
|
|
\f[C]link\f[] and \f[C]rename\f[]\[aq]s behaviors since
|
|
\f[C]ignorepponrename\f[] is available to disable that behavior.
|
|
In mergerfs v3.0 the path preserving behavior of rename and link will
|
|
likely be separated from the policy all together.
|
|
.SS Policy descriptions
|
|
.PP
|
|
A policy\[aq]s behavior differs, as mentioned above, based on the
|
|
function it is used with.
|
|
Sometimes it really might not make sense to even offer certain policies
|
|
because they are literally the same as others but it makes things a bit
|
|
more uniform.
|
|
In mergerfs 3.0 this might change.
|
|
.PP
|
|
.TS
|
|
tab(@);
|
|
lw(16.6n) lw(53.4n).
|
|
T{
|
|
Policy
|
|
T}@T{
|
|
Description
|
|
T}
|
|
_
|
|
T{
|
|
all
|
|
T}@T{
|
|
Search: Same as \f[B]epall\f[].
|
|
Action: Same as \f[B]epall\f[].
|
|
Create: for \f[B]mkdir\f[], \f[B]mknod\f[], and \f[B]symlink\f[] it will
|
|
apply to all branches.
|
|
\f[B]create\f[] works like \f[B]ff\f[].
|
|
T}
|
|
T{
|
|
epall (existing path, all)
|
|
T}@T{
|
|
Search: Same as \f[B]epff\f[] (but more expensive because it doesn\[aq]t
|
|
stop after finding a valid branch).
|
|
Action: apply to all found.
|
|
Create: for \f[B]mkdir\f[], \f[B]mknod\f[], and \f[B]symlink\f[] it will
|
|
apply to all found.
|
|
\f[B]create\f[] works like \f[B]epff\f[] (but more expensive because it
|
|
doesn\[aq]t stop after finding a valid branch).
|
|
T}
|
|
T{
|
|
epff (existing path, first found)
|
|
T}@T{
|
|
Given the order of the branches, as defined at mount time or configured
|
|
at runtime, act on the first one found where the relative path exists.
|
|
T}
|
|
T{
|
|
eplfs (existing path, least free space)
|
|
T}@T{
|
|
Of all the branches on which the relative path exists choose the drive
|
|
with the least free space.
|
|
T}
|
|
T{
|
|
eplus (existing path, least used space)
|
|
T}@T{
|
|
Of all the branches on which the relative path exists choose the drive
|
|
with the least used space.
|
|
T}
|
|
T{
|
|
epmfs (existing path, most free space)
|
|
T}@T{
|
|
Of all the branches on which the relative path exists choose the drive
|
|
with the most free space.
|
|
T}
|
|
T{
|
|
eppfrd (existing path, percentage free random distribution)
|
|
T}@T{
|
|
Like \f[B]pfrd\f[] but limited to existing paths.
|
|
T}
|
|
T{
|
|
eprand (existing path, random)
|
|
T}@T{
|
|
Calls \f[B]epall\f[] and then randomizes.
|
|
Returns 1.
|
|
T}
|
|
T{
|
|
erofs
|
|
T}@T{
|
|
Exclusively return \f[B]\-1\f[] with \f[B]errno\f[] set to
|
|
\f[B]EROFS\f[] (read\-only filesystem).
|
|
T}
|
|
T{
|
|
ff (first found)
|
|
T}@T{
|
|
Search: Same as \f[B]epff\f[].
|
|
Action: Same as \f[B]epff\f[].
|
|
Create: Given the order of the drives, as defined at mount time or
|
|
configured at runtime, act on the first one found.
|
|
T}
|
|
T{
|
|
lfs (least free space)
|
|
T}@T{
|
|
Search: Same as \f[B]eplfs\f[].
|
|
Action: Same as \f[B]eplfs\f[].
|
|
Create: Pick the drive with the least available free space.
|
|
T}
|
|
T{
|
|
lus (least used space)
|
|
T}@T{
|
|
Search: Same as \f[B]eplus\f[].
|
|
Action: Same as \f[B]eplus\f[].
|
|
Create: Pick the drive with the least used space.
|
|
T}
|
|
T{
|
|
mfs (most free space)
|
|
T}@T{
|
|
Search: Same as \f[B]epmfs\f[].
|
|
Action: Same as \f[B]epmfs\f[].
|
|
Create: Pick the drive with the most available free space.
|
|
T}
|
|
T{
|
|
msplfs (most shared path, least free space)
|
|
T}@T{
|
|
Search: Same as \f[B]eplfs\f[].
|
|
Action: Same as \f[B]eplfs\f[].
|
|
Create: like \f[B]eplfs\f[] but walk back the path if it fails to find a
|
|
branch at that level.
|
|
T}
|
|
T{
|
|
msplus (most shared path, least used space)
|
|
T}@T{
|
|
Search: Same as \f[B]eplus\f[].
|
|
Action: Same as \f[B]eplus\f[].
|
|
Create: like \f[B]eplus\f[] but walk back the path if it fails to find a
|
|
branch at that level.
|
|
T}
|
|
T{
|
|
mspmfs (most shared path, most free space)
|
|
T}@T{
|
|
Search: Same as \f[B]epmfs\f[].
|
|
Action: Same as \f[B]epmfs\f[].
|
|
Create: like \f[B]epmfs\f[] but walk back the path if it fails to find a
|
|
branch at that level.
|
|
T}
|
|
T{
|
|
msppfrd (most shared path, percentage free random distribution)
|
|
T}@T{
|
|
Search: Same as \f[B]eppfrd\f[].
|
|
Action: Same as \f[B]eppfrd\f[].
|
|
Create: Like \f[B]eppfrd\f[] but will walk back the path if it fails to
|
|
find a branch at that level.
|
|
T}
|
|
T{
|
|
newest
|
|
T}@T{
|
|
Pick the file / directory with the largest mtime.
|
|
T}
|
|
T{
|
|
pfrd (percentage free random distribution)
|
|
T}@T{
|
|
Search: Same as \f[B]eppfrd\f[].
|
|
Action: Same as \f[B]eppfrd\f[].
|
|
Create: Chooses a branch at random with the likelihood of selection
|
|
based on a branch\[aq]s available space relative to the total.
|
|
T}
|
|
T{
|
|
rand (random)
|
|
T}@T{
|
|
Calls \f[B]all\f[] and then randomizes.
|
|
Returns 1.
|
|
T}
|
|
.TE
|
|
.PP
|
|
\f[B]NOTE:\f[] If you are using an underlying filesystem that reserves
|
|
blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the
|
|
reservation by using \f[C]f_bavail\f[] (number of free blocks for
|
|
unprivileged users) rather than \f[C]f_bfree\f[] (number of free blocks)
|
|
in policy calculations.
|
|
\f[B]df\f[] does NOT use \f[C]f_bavail\f[], it uses \f[C]f_bfree\f[], so
|
|
direct comparisons between \f[B]df\f[] output and mergerfs\[aq] policies
|
|
is not appropriate.
|
|
.SS Defaults
|
|
.PP
|
|
.TS
|
|
tab(@);
|
|
l l.
|
|
T{
|
|
Category
|
|
T}@T{
|
|
Policy
|
|
T}
|
|
_
|
|
T{
|
|
action
|
|
T}@T{
|
|
epall
|
|
T}
|
|
T{
|
|
create
|
|
T}@T{
|
|
epmfs
|
|
T}
|
|
T{
|
|
search
|
|
T}@T{
|
|
ff
|
|
T}
|
|
.TE
|
|
.SS ioctl
|
|
.PP
|
|
When \f[C]ioctl\f[] is used with an open file then it will use the file
|
|
handle which was created at the original \f[C]open\f[] call.
|
|
However, when using \f[C]ioctl\f[] with a directory mergerfs will use
|
|
the \f[C]open\f[] policy to find the directory to act on.
|
|
.SS rename & link
|
|
.PP
|
|
\f[B]NOTE:\f[] If you\[aq]re receiving errors from software when files
|
|
are moved / renamed / linked then you should consider changing the
|
|
create policy to one which is \f[B]not\f[] path preserving, enabling
|
|
\f[C]ignorepponrename\f[], or contacting the author of the offending
|
|
software and requesting that \f[C]EXDEV\f[] (cross device / improper
|
|
link) be properly handled.
|
|
.PP
|
|
\f[C]rename\f[] and \f[C]link\f[] are tricky functions in a union
|
|
filesystem.
|
|
\f[C]rename\f[] only works within a single filesystem or device.
|
|
If a rename can\[aq]t be done atomically due to the source and
|
|
destination paths existing on different mount points it will return
|
|
\f[B]\-1\f[] with \f[B]errno = EXDEV\f[] (cross device / improper link).
|
|
So if a \f[C]rename\f[]\[aq]s source and target are on different drives
|
|
within the pool it creates an issue.
|
|
.PP
|
|
Originally mergerfs would return EXDEV whenever a rename was requested
|
|
which was cross directory in any way.
|
|
This made the code simple and was technically compliant with POSIX
|
|
requirements.
|
|
However, many applications fail to handle EXDEV at all and treat it as a
|
|
normal error or otherwise handle it poorly.
|
|
Such apps include: gvfsd\-fuse v1.20.3 and prior, Finder / CIFS/SMB
|
|
client in Apple OSX 10.9+, NZBGet, Samba\[aq]s recycling bin feature.
|
|
.PP
|
|
As a result a compromise was made in order to get most software to work
|
|
while still obeying mergerfs\[aq] policies.
|
|
Below is the basic logic.
|
|
.IP \[bu] 2
|
|
If using a \f[B]create\f[] policy which tries to preserve directory
|
|
paths (epff,eplfs,eplus,epmfs)
|
|
.IP \[bu] 2
|
|
Using the \f[B]rename\f[] policy get the list of files to rename
|
|
.IP \[bu] 2
|
|
For each file attempt rename:
|
|
.RS 2
|
|
.IP \[bu] 2
|
|
If failure with ENOENT (no such file or directory) run \f[B]create\f[]
|
|
policy
|
|
.IP \[bu] 2
|
|
If create policy returns the same drive as currently evaluating then
|
|
clone the path
|
|
.IP \[bu] 2
|
|
Re\-attempt rename
|
|
.RE
|
|
.IP \[bu] 2
|
|
If \f[B]any\f[] of the renames succeed the higher level rename is
|
|
considered a success
|
|
.IP \[bu] 2
|
|
If \f[B]no\f[] renames succeed the first error encountered will be
|
|
returned
|
|
.IP \[bu] 2
|
|
On success:
|
|
.RS 2
|
|
.IP \[bu] 2
|
|
Remove the target from all drives with no source file
|
|
.IP \[bu] 2
|
|
Remove the source from all drives which failed to rename
|
|
.RE
|
|
.IP \[bu] 2
|
|
If using a \f[B]create\f[] policy which does \f[B]not\f[] try to
|
|
preserve directory paths
|
|
.IP \[bu] 2
|
|
Using the \f[B]rename\f[] policy get the list of files to rename
|
|
.IP \[bu] 2
|
|
Using the \f[B]getattr\f[] policy get the target path
|
|
.IP \[bu] 2
|
|
For each file attempt rename:
|
|
.RS 2
|
|
.IP \[bu] 2
|
|
If the source drive != target drive:
|
|
.IP \[bu] 2
|
|
Clone target path from target drive to source drive
|
|
.IP \[bu] 2
|
|
Rename
|
|
.RE
|
|
.IP \[bu] 2
|
|
If \f[B]any\f[] of the renames succeed the higher level rename is
|
|
considered a success
|
|
.IP \[bu] 2
|
|
If \f[B]no\f[] renames succeed the first error encountered will be
|
|
returned
|
|
.IP \[bu] 2
|
|
On success:
|
|
.RS 2
|
|
.IP \[bu] 2
|
|
Remove the target from all drives with no source file
|
|
.IP \[bu] 2
|
|
Remove the source from all drives which failed to rename
|
|
.RE
|
|
.PP
|
|
The the removals are subject to normal entitlement checks.
|
|
.PP
|
|
The above behavior will help minimize the likelihood of EXDEV being
|
|
returned but it will still be possible.
|
|
.PP
|
|
\f[B]link\f[] uses the same strategy but without the removals.
|
|
.SS readdir
|
|
.PP
|
|
readdir (http://linux.die.net/man/3/readdir) is different from all other
|
|
filesystem functions.
|
|
While it could have its own set of policies to tweak its behavior at
|
|
this time it provides a simple union of files and directories found.
|
|
Remember that any action or information queried about these files and
|
|
directories come from the respective function.
|
|
For instance: an \f[B]ls\f[] is a \f[B]readdir\f[] and for each
|
|
file/directory returned \f[B]getattr\f[] is called.
|
|
Meaning the policy of \f[B]getattr\f[] is responsible for choosing the
|
|
file/directory which is the source of the metadata you see in an
|
|
\f[B]ls\f[].
|
|
.SS statfs / statvfs
|
|
.PP
|
|
statvfs (http://linux.die.net/man/2/statvfs) normalizes the source
|
|
drives based on the fragment size and sums the number of adjusted blocks
|
|
and inodes.
|
|
This means you will see the combined space of all sources.
|
|
Total, used, and free.
|
|
The sources however are dedupped based on the drive so multiple sources
|
|
on the same drive will not result in double counting its space.
|
|
Filesystems mounted further down the tree of the branch will not be
|
|
included when checking the mount\[aq]s stats.
|
|
.PP
|
|
The options \f[C]statfs\f[] and \f[C]statfs_ignore\f[] can be used to
|
|
modify \f[C]statfs\f[] behavior.
|
|
.SH ERROR HANDLING
|
|
.PP
|
|
POSIX filesystem functions offer a single return code meaning that there
|
|
is some complication regarding the handling of multiple branches as
|
|
mergerfs does.
|
|
It tries to handle errors in a way that would generally return
|
|
meaningful values for that particular function.
|
|
.SS chmod, chown, removexattr, setxattr, truncate, utimens
|
|
.IP "1)" 3
|
|
if no error: return 0 (success)
|
|
.IP "2)" 3
|
|
if no successes: return first error
|
|
.IP "3)" 3
|
|
if one of the files acted on was the same as the related search
|
|
function: return its value
|
|
.IP "4)" 3
|
|
return 0 (success)
|
|
.PP
|
|
While doing this increases the complexity and cost of error handling,
|
|
particularly step 3, this provides probably the most reasonable return
|
|
value.
|
|
.SS unlink, rmdir
|
|
.IP "1)" 3
|
|
if no errors: return 0 (success)
|
|
.IP "2)" 3
|
|
return first error
|
|
.PP
|
|
Older version of mergerfs would return success if any success occurred
|
|
but for unlink and rmdir there are downstream assumptions that, while
|
|
not impossible to occur, can confuse some software.
|
|
.SS others
|
|
.PP
|
|
For search functions there is always a single thing acted on and as such
|
|
whatever return value that comes from the single function call is
|
|
returned.
|
|
.PP
|
|
For create functions \f[C]mkdir\f[], \f[C]mknod\f[], and
|
|
\f[C]symlink\f[] which don\[aq]t return a file descriptor and therefore
|
|
can have \f[C]all\f[] or \f[C]epall\f[] policies it will return success
|
|
if any of the calls succeed and an error otherwise.
|
|
.SH BUILD / UPDATE
|
|
.PP
|
|
\f[B]NOTE:\f[] Prebuilt packages can be found at and recommended for
|
|
most users: https://github.com/trapexit/mergerfs/releases \f[B]NOTE:\f[]
|
|
Only tagged releases are supported.
|
|
\f[C]master\f[] and other branches should be considered works in
|
|
progress.
|
|
.PP
|
|
First get the code from github (https://github.com/trapexit/mergerfs).
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ git\ clone\ https://github.com/trapexit/mergerfs.git
|
|
$\ #\ or
|
|
$\ wget\ https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs\-<ver>.tar.gz
|
|
\f[]
|
|
.fi
|
|
.SS Debian / Ubuntu
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ cd\ mergerfs
|
|
$\ sudo\ tools/install\-build\-pkgs
|
|
$\ make\ deb
|
|
$\ sudo\ dpkg\ \-i\ ../mergerfs_<version>_<arch>.deb
|
|
\f[]
|
|
.fi
|
|
.SS RHEL / CentOS /Fedora
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ su\ \-
|
|
#\ cd\ mergerfs
|
|
#\ tools/install\-build\-pkgs
|
|
#\ make\ rpm
|
|
#\ rpm\ \-i\ rpmbuild/RPMS/<arch>/mergerfs\-<version>.<arch>.rpm
|
|
\f[]
|
|
.fi
|
|
.SS Generically
|
|
.PP
|
|
Have git, g++, make, python installed.
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ cd\ mergerfs
|
|
$\ make
|
|
$\ sudo\ make\ install
|
|
\f[]
|
|
.fi
|
|
.SS Build options
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ make\ help
|
|
usage:\ make
|
|
|
|
make\ USE_XATTR=0\ \ \ \ \ \ \-\ build\ program\ without\ xattrs\ functionality
|
|
make\ STATIC=1\ \ \ \ \ \ \ \ \ \-\ build\ static\ binary
|
|
make\ LTO=1\ \ \ \ \ \ \ \ \ \ \ \ \-\ build\ with\ link\ time\ optimization
|
|
\f[]
|
|
.fi
|
|
.SH UPGRADE
|
|
.PP
|
|
mergerfs can be upgraded live by mounting on top of the previous
|
|
instance.
|
|
Simply install the new version of mergerfs and follow the instructions
|
|
below.
|
|
.PP
|
|
Add \f[C]nonempty\f[] to your mergerfs option list and call mergerfs
|
|
again or if using \f[C]/etc/fstab\f[] call for it to mount again.
|
|
Existing open files and such will continue to work fine though they
|
|
won\[aq]t see runtime changes since any such change would be the new
|
|
mount.
|
|
If you plan on changing settings with the new mount you should / could
|
|
apply those before mounting the new version.
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ sudo\ mount\ /mnt/mergerfs
|
|
$\ mount\ |\ grep\ mergerfs
|
|
media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
|
media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
A problem with this approach is that the underlying instance will
|
|
continue to run even if the software using it stop or are restarted.
|
|
To work around this you can use a "lazy umount".
|
|
Before mounting over top the mount point with the new instance of
|
|
mergerfs issue: \f[C]umount\ \-l\ <mergerfs_mountpoint>\f[].
|
|
.SH RUNTIME CONFIG
|
|
.SS ioctl
|
|
.PP
|
|
The original runtime config API was via xattr calls.
|
|
This however became an issue when needing to disable xattr.
|
|
While slightly less convenient ioctl does not have the same problems and
|
|
will be the main API going forward.
|
|
.PP
|
|
The keys are the same as the command line option arguments as well as
|
|
the config file.
|
|
.SS requests / commands
|
|
.PP
|
|
All commands take a 4096 byte char buffer.
|
|
.IP \[bu] 2
|
|
read keys: get a nul \[aq]\[aq] delimited list of option keys
|
|
.IP \[bu] 2
|
|
_IOWR(0xDF,0,char[4096]) = 0xD000DF00
|
|
.IP \[bu] 2
|
|
on success ioctl return value is the total length
|
|
.IP \[bu] 2
|
|
read value: get an option value
|
|
.IP \[bu] 2
|
|
_IOWR(0xDF,1,char[4096]) = 0xD000DF01
|
|
.IP \[bu] 2
|
|
the key is passed in via the char buffer as a nul \[aq]\[aq] terminated
|
|
string
|
|
.IP \[bu] 2
|
|
on success ioctl return value is the total length
|
|
.IP \[bu] 2
|
|
write value: set an option value
|
|
.IP \[bu] 2
|
|
_IOW(0xDF,2,char[4096]) = 0x5000DF02
|
|
.IP \[bu] 2
|
|
the key and value is passed in via the char buffer as a nul \[aq]\[aq]
|
|
terminated string in the format of \f[C]key=value\f[]
|
|
.IP \[bu] 2
|
|
on success ioctl return value is 0
|
|
.IP \[bu] 2
|
|
file info: get mergerfs metadata info for a file
|
|
.IP \[bu] 2
|
|
_IOWR(0xDF,3,char[4096]) = 0xD000DF03
|
|
.IP \[bu] 2
|
|
the key is passed in via the char buffer as a nul \[aq]\[aq] terminated
|
|
string
|
|
.IP \[bu] 2
|
|
on success the ioctl return value is the total length
|
|
.IP \[bu] 2
|
|
keys:
|
|
.RS 2
|
|
.IP \[bu] 2
|
|
basepath: the base mount point for the file according to the getattr
|
|
policy
|
|
.IP \[bu] 2
|
|
relpath: the relative path of the file from the mount point
|
|
.IP \[bu] 2
|
|
fullpath: the full path of the underlying file according to the getattr
|
|
policy
|
|
.IP \[bu] 2
|
|
allpaths: a NUL \[aq]\[aq] delimited list of full paths to all files
|
|
found
|
|
.RE
|
|
.SS .mergerfs pseudo file (deprecated)
|
|
.PP
|
|
NOTE: this interface will be removed in mergerfs 3.0
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
<mountpoint>/.mergerfs
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
There is a pseudo file available at the mount point which allows for the
|
|
runtime modification of certain \f[B]mergerfs\f[] options.
|
|
The file will not show up in \f[B]readdir\f[] but can be
|
|
\f[B]stat\f[]\[aq]ed and manipulated via
|
|
{list,get,set}xattrs (http://linux.die.net/man/2/listxattr) calls.
|
|
.PP
|
|
Any changes made at runtime are \f[B]not\f[] persisted.
|
|
If you wish for values to persist they must be included as options
|
|
wherever you configure the mounting of mergerfs (/etc/fstab).
|
|
.SS Keys
|
|
.PP
|
|
Use \f[C]xattr\ \-l\ /mountpoint/.mergerfs\f[] to see all supported
|
|
keys.
|
|
Some are informational and therefore read\-only.
|
|
\f[C]setxattr\f[] will return EINVAL (invalid argument) on read\-only
|
|
keys.
|
|
.SS Values
|
|
.PP
|
|
Same as the command line.
|
|
.SS user.mergerfs.branches
|
|
.PP
|
|
\f[B]NOTE:\f[] formerly \f[C]user.mergerfs.srcmounts\f[] but said key is
|
|
still supported.
|
|
.PP
|
|
Used to query or modify the list of branches.
|
|
When modifying there are several shortcuts to easy manipulation of the
|
|
list.
|
|
.PP
|
|
.TS
|
|
tab(@);
|
|
l l.
|
|
T{
|
|
Value
|
|
T}@T{
|
|
Description
|
|
T}
|
|
_
|
|
T{
|
|
[list]
|
|
T}@T{
|
|
set
|
|
T}
|
|
T{
|
|
+<[list]
|
|
T}@T{
|
|
prepend
|
|
T}
|
|
T{
|
|
+>[list]
|
|
T}@T{
|
|
append
|
|
T}
|
|
T{
|
|
\-[list]
|
|
T}@T{
|
|
remove all values provided
|
|
T}
|
|
T{
|
|
\-<
|
|
T}@T{
|
|
remove first in list
|
|
T}
|
|
T{
|
|
\->
|
|
T}@T{
|
|
remove last in list
|
|
T}
|
|
.TE
|
|
.PP
|
|
\f[C]xattr\ \-w\ user.mergerfs.branches\ +</mnt/drive3\ /mnt/pool/.mergerfs\f[]
|
|
.PP
|
|
The \f[C]=NC\f[], \f[C]=RO\f[], \f[C]=RW\f[] syntax works just as on the
|
|
command line.
|
|
.SS Example
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-l\ .mergerfs
|
|
user.mergerfs.branches:\ /mnt/a=RW:/mnt/b=RW
|
|
user.mergerfs.minfreespace:\ 4294967295
|
|
user.mergerfs.moveonenospc:\ false
|
|
\&...
|
|
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.category.search\ .mergerfs
|
|
ff
|
|
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.category.search\ newest\ .mergerfs
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.category.search\ .mergerfs
|
|
newest
|
|
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.branches\ +/mnt/c\ .mergerfs
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.branches\ .mergerfs
|
|
/mnt/a:/mnt/b:/mnt/c
|
|
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.branches\ =/mnt/c\ .mergerfs
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.branches\ .mergerfs
|
|
/mnt/c
|
|
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.branches\ \[aq]+</mnt/a:/mnt/b\[aq]\ .mergerfs
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.branches\ .mergerfs
|
|
/mnt/a:/mnt/b:/mnt/c
|
|
\f[]
|
|
.fi
|
|
.SS file / directory xattrs
|
|
.PP
|
|
While they won\[aq]t show up when using
|
|
listxattr (http://linux.die.net/man/2/listxattr) \f[B]mergerfs\f[]
|
|
offers a number of special xattrs to query information about the files
|
|
served.
|
|
To access the values you will need to issue a
|
|
getxattr (http://linux.die.net/man/2/getxattr) for one of the following:
|
|
.IP \[bu] 2
|
|
\f[B]user.mergerfs.basepath\f[]: the base mount point for the file given
|
|
the current getattr policy
|
|
.IP \[bu] 2
|
|
\f[B]user.mergerfs.relpath\f[]: the relative path of the file from the
|
|
perspective of the mount point
|
|
.IP \[bu] 2
|
|
\f[B]user.mergerfs.fullpath\f[]: the full path of the original file
|
|
given the getattr policy
|
|
.IP \[bu] 2
|
|
\f[B]user.mergerfs.allpaths\f[]: a NUL (\[aq]\[aq]) separated list of
|
|
full paths to all files found
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
[trapexit:/mnt/mergerfs]\ $\ ls
|
|
A\ B\ C
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.fullpath\ A
|
|
/mnt/a/full/path/to/A
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.basepath\ A
|
|
/mnt/a
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.relpath\ A
|
|
/full/path/to/A
|
|
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.allpaths\ A\ |\ tr\ \[aq]\\0\[aq]\ \[aq]\\n\[aq]
|
|
/mnt/a/full/path/to/A
|
|
/mnt/b/full/path/to/A
|
|
\f[]
|
|
.fi
|
|
.SH TOOLING
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/mergerfs\-tools
|
|
.IP \[bu] 2
|
|
mergerfs.ctl: A tool to make it easier to query and configure mergerfs
|
|
at runtime
|
|
.IP \[bu] 2
|
|
mergerfs.fsck: Provides permissions and ownership auditing and the
|
|
ability to fix them
|
|
.IP \[bu] 2
|
|
mergerfs.dedup: Will help identify and optionally remove duplicate files
|
|
.IP \[bu] 2
|
|
mergerfs.dup: Ensure there are at least N copies of a file across the
|
|
pool
|
|
.IP \[bu] 2
|
|
mergerfs.balance: Rebalance files across drives by moving them from the
|
|
most filled to the least filled
|
|
.IP \[bu] 2
|
|
mergerfs.consolidate: move files within a single mergerfs directory to
|
|
the drive with most free space
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/scorch
|
|
.IP \[bu] 2
|
|
scorch: A tool to help discover silent corruption of files and keep
|
|
track of files
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/bbf
|
|
.IP \[bu] 2
|
|
bbf (bad block finder): a tool to scan for and \[aq]fix\[aq] hard drive
|
|
bad blocks and find the files using those blocks
|
|
.SH CACHING
|
|
.SS page caching
|
|
.PP
|
|
https://en.wikipedia.org/wiki/Page_cache
|
|
.PP
|
|
tl;dr: * cache.files=off: Disables page caching.
|
|
Underlying files cached, mergerfs files are not.
|
|
* cache.files=partial: Enables page caching.
|
|
Underlying files cached, mergerfs files cached while open.
|
|
* cache.files=full: Enables page caching.
|
|
Underlying files cached, mergerfs files cached across opens.
|
|
* cache.files=auto\-full: Enables page caching.
|
|
Underlying files cached, mergerfs files cached across opens if mtime and
|
|
size are unchanged since previous open.
|
|
* cache.files=libfuse: follow traditional libfuse \f[C]direct_io\f[],
|
|
\f[C]kernel_cache\f[], and \f[C]auto_cache\f[] arguments.
|
|
.PP
|
|
FUSE, which mergerfs uses, offers a number of page caching modes.
|
|
mergerfs tries to simplify their use via the \f[C]cache.files\f[]
|
|
option.
|
|
It can and should replace usage of \f[C]direct_io\f[],
|
|
\f[C]kernel_cache\f[], and \f[C]auto_cache\f[].
|
|
.PP
|
|
Due to mergerfs using FUSE and therefore being a userland process
|
|
proxying existing filesystems the kernel will double cache the content
|
|
being read and written through mergerfs.
|
|
Once from the underlying filesystem and once from mergerfs (it sees them
|
|
as two separate entities).
|
|
Using \f[C]cache.files=off\f[] will keep the double caching from
|
|
happening by disabling caching of mergerfs but this has the side effect
|
|
that \f[I]all\f[] read and write calls will be passed to mergerfs which
|
|
may be slower than enabling caching, you lose shared \f[C]mmap\f[]
|
|
support which can affect apps such as rtorrent, and no read\-ahead will
|
|
take place.
|
|
The kernel will still cache the underlying filesystem data but that only
|
|
helps so much given mergerfs will still process all requests.
|
|
.PP
|
|
If you do enable file page caching,
|
|
\f[C]cache.files=partial|full|auto\-full\f[], you should also enable
|
|
\f[C]dropcacheonclose\f[] which will cause mergerfs to instruct the
|
|
kernel to flush the underlying file\[aq]s page cache when the file is
|
|
closed.
|
|
This behavior is the same as the rsync fadvise / drop cache patch and
|
|
Feh\[aq]s nocache project.
|
|
.PP
|
|
If most files are read once through and closed (like media) it is best
|
|
to enable \f[C]dropcacheonclose\f[] regardless of caching mode in order
|
|
to minimize buffer bloat.
|
|
.PP
|
|
It is difficult to balance memory usage, cache bloat & duplication, and
|
|
performance.
|
|
Ideally mergerfs would be able to disable caching for the files it
|
|
reads/writes but allow page caching for itself.
|
|
That would limit the FUSE overhead.
|
|
However, there isn\[aq]t a good way to achieve this.
|
|
It would need to open all files with O_DIRECT which places limitations
|
|
on the what underlying filesystems would be supported and complicates
|
|
the code.
|
|
.PP
|
|
kernel documentation:
|
|
https://www.kernel.org/doc/Documentation/filesystems/fuse\-io.txt
|
|
.SS entry & attribute caching
|
|
.PP
|
|
Given the relatively high cost of FUSE due to the kernel <\-> userspace
|
|
round trips there are kernel side caches for file entries and
|
|
attributes.
|
|
The entry cache limits the \f[C]lookup\f[] calls to mergerfs which ask
|
|
if a file exists.
|
|
The attribute cache limits the need to make \f[C]getattr\f[] calls to
|
|
mergerfs which provide file attributes (mode, size, type, etc.).
|
|
As with the page cache these should not be used if the underlying
|
|
filesystems are being manipulated at the same time as it could lead to
|
|
odd behavior or data corruption.
|
|
The options for setting these are \f[C]cache.entry\f[] and
|
|
\f[C]cache.negative_entry\f[] for the entry cache and
|
|
\f[C]cache.attr\f[] for the attributes cache.
|
|
\f[C]cache.negative_entry\f[] refers to the timeout for negative
|
|
responses to lookups (non\-existent files).
|
|
.SS writeback caching
|
|
.PP
|
|
When \f[C]cache.files\f[] is enabled the default is for it to perform
|
|
writethrough caching.
|
|
This behavior won\[aq]t help improve performance as each write still
|
|
goes one for one through the filesystem.
|
|
By enabling the FUSE writeback cache small writes may be aggregated by
|
|
the kernel and then sent to mergerfs as one larger request.
|
|
This can greatly improve the throughput for apps which write to files
|
|
inefficiently.
|
|
The amount the kernel can aggregate is limited by the size of a FUSE
|
|
message.
|
|
Read the \f[C]fuse_msg_size\f[] section for more details.
|
|
.PP
|
|
There is a small side effect as a result of enabling writeback caching.
|
|
Underlying files won\[aq]t ever be opened with O_APPEND or O_WRONLY.
|
|
The former because the kernel then manages append mode and the latter
|
|
because the kernel may request file data from mergerfs to populate the
|
|
write cache.
|
|
The O_APPEND change means that if a file is changed outside of mergerfs
|
|
it could lead to corruption as the kernel won\[aq]t know the end of the
|
|
file has changed.
|
|
That said any time you use caching you should keep from using the same
|
|
file outside of mergerfs at the same time.
|
|
.PP
|
|
Note that if an application is properly sizing writes then writeback
|
|
caching will have little or no effect.
|
|
It will only help with writes of sizes below the FUSE message size (128K
|
|
on older kernels, 1M on newer).
|
|
.SS policy caching
|
|
.PP
|
|
Policies are run every time a function (with a policy as mentioned
|
|
above) is called.
|
|
These policies can be expensive depending on mergerfs\[aq] setup and
|
|
client usage patterns.
|
|
Generally we wouldn\[aq]t want to cache policy results because it may
|
|
result in stale responses if the underlying drives are used directly.
|
|
.PP
|
|
The \f[C]open\f[] policy cache will cache the result of an \f[C]open\f[]
|
|
policy for a particular input for \f[C]cache.open\f[] seconds or until
|
|
the file is unlinked.
|
|
Each file close (release) will randomly chose to clean up the cache of
|
|
expired entries.
|
|
.PP
|
|
This cache is really only useful in cases where you have a large number
|
|
of branches and \f[C]open\f[] is called on the same files repeatedly
|
|
(like \f[B]Transmission\f[] which opens and closes a file on every
|
|
read/write presumably to keep file handle usage low).
|
|
.SS statfs caching
|
|
.PP
|
|
Of the syscalls used by mergerfs in policies the \f[C]statfs\f[] /
|
|
\f[C]statvfs\f[] call is perhaps the most expensive.
|
|
It\[aq]s used to find out the available space of a drive and whether it
|
|
is mounted read\-only.
|
|
Depending on the setup and usage pattern these queries can be relatively
|
|
costly.
|
|
When \f[C]cache.statfs\f[] is enabled all calls to \f[C]statfs\f[] by a
|
|
policy will be cached for the number of seconds its set to.
|
|
.PP
|
|
Example: If the create policy is \f[C]mfs\f[] and the timeout is 60 then
|
|
for that 60 seconds the same drive will be returned as the target for
|
|
creates because the available space won\[aq]t be updated for that time.
|
|
.SS symlink caching
|
|
.PP
|
|
As of version 4.20 Linux supports symlink caching.
|
|
Significant performance increases can be had in workloads which use a
|
|
lot of symlinks.
|
|
Setting \f[C]cache.symlinks=true\f[] will result in requesting symlink
|
|
caching from the kernel only if supported.
|
|
As a result its safe to enable it on systems prior to 4.20.
|
|
That said it is disabled by default for now.
|
|
You can see if caching is enabled by querying the xattr
|
|
\f[C]user.mergerfs.cache.symlinks\f[] but given it must be requested at
|
|
startup you can not change it at runtime.
|
|
.SS readdir caching
|
|
.PP
|
|
As of version 4.20 Linux supports readdir caching.
|
|
This can have a significant impact on directory traversal.
|
|
Especially when combined with entry (\f[C]cache.entry\f[]) and attribute
|
|
(\f[C]cache.attr\f[]) caching.
|
|
Setting \f[C]cache.readdir=true\f[] will result in requesting readdir
|
|
caching from the kernel on each \f[C]opendir\f[].
|
|
If the kernel doesn\[aq]t support readdir caching setting the option to
|
|
\f[C]true\f[] has no effect.
|
|
This option is configurable at runtime via xattr
|
|
\f[C]user.mergerfs.cache.readdir\f[].
|
|
.SS tiered caching
|
|
.PP
|
|
Some storage technologies support what some call "tiered" caching.
|
|
The placing of usually smaller, faster storage as a transparent cache to
|
|
larger, slower storage.
|
|
NVMe, SSD, Optane in front of traditional HDDs for instance.
|
|
.PP
|
|
MergerFS does not natively support any sort of tiered caching.
|
|
Most users have no use for such a feature and its inclusion would
|
|
complicate the code.
|
|
However, there are a few situations where a cache drive could help with
|
|
a typical mergerfs setup.
|
|
.IP "1." 3
|
|
Fast network, slow drives, many readers: You\[aq]ve a 10+Gbps network
|
|
with many readers and your regular drives can\[aq]t keep up.
|
|
.IP "2." 3
|
|
Fast network, slow drives, small\[aq]ish bursty writes: You have a
|
|
10+Gbps network and wish to transfer amounts of data less than your
|
|
cache drive but wish to do so quickly.
|
|
.PP
|
|
With #1 its arguable if you should be using mergerfs at all.
|
|
RAID would probably be the better solution.
|
|
If you\[aq]re going to use mergerfs there are other tactics that may
|
|
help: spreading the data across drives (see the mergerfs.dup tool) and
|
|
setting \f[C]func.open=rand\f[], using \f[C]symlinkify\f[], or using
|
|
dm\-cache or a similar technology to add tiered cache to the underlying
|
|
device.
|
|
.PP
|
|
With #2 one could use dm\-cache as well but there is another solution
|
|
which requires only mergerfs and a cronjob.
|
|
.IP "1." 3
|
|
Create 2 mergerfs pools.
|
|
One which includes just the slow drives and one which has both the fast
|
|
drives (SSD,NVME,etc.) and slow drives.
|
|
.IP "2." 3
|
|
The \[aq]cache\[aq] pool should have the cache drives listed first.
|
|
.IP "3." 3
|
|
The best \f[C]create\f[] policies to use for the \[aq]cache\[aq] pool
|
|
would probably be \f[C]ff\f[], \f[C]epff\f[], \f[C]lfs\f[], or
|
|
\f[C]eplfs\f[].
|
|
The latter two under the assumption that the cache drive(s) are far
|
|
smaller than the backing drives.
|
|
If using path preserving policies remember that you\[aq]ll need to
|
|
manually create the core directories of those paths you wish to be
|
|
cached.
|
|
Be sure the permissions are in sync.
|
|
Use \f[C]mergerfs.fsck\f[] to check / correct them.
|
|
You could also tag the slow drives as \f[C]=NC\f[] though that\[aq]d
|
|
mean if the cache drives fill you\[aq]d get "out of space" errors.
|
|
.IP "4." 3
|
|
Enable \f[C]moveonenospc\f[] and set \f[C]minfreespace\f[]
|
|
appropriately.
|
|
To make sure there is enough room on the "slow" pool you might want to
|
|
set \f[C]minfreespace\f[] to at least as large as the size of the
|
|
largest cache drive if not larger.
|
|
This way in the worst case the whole of the cache drive(s) can be moved
|
|
to the other drives.
|
|
.IP "5." 3
|
|
Set your programs to use the cache pool.
|
|
.IP "6." 3
|
|
Save one of the below scripts or create you\[aq]re own.
|
|
.IP "7." 3
|
|
Use \f[C]cron\f[] (as root) to schedule the command at whatever
|
|
frequency is appropriate for your workflow.
|
|
.SS time based expiring
|
|
.PP
|
|
Move files from cache to backing pool based only on the last time the
|
|
file was accessed.
|
|
Replace \f[C]\-atime\f[] with \f[C]\-amin\f[] if you want minutes rather
|
|
than days.
|
|
May want to use the \f[C]fadvise\f[] / \f[C]\-\-drop\-cache\f[] version
|
|
of rsync or run rsync with the tool "nocache".
|
|
.PP
|
|
\f[I]NOTE:\f[] The arguments to these scripts include the cache
|
|
\f[B]drive\f[].
|
|
Not the pool with the cache drive.
|
|
You could have data loss if the source is the cache pool.
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
#!/bin/bash
|
|
|
|
if\ [\ $#\ !=\ 3\ ];\ then
|
|
\ \ echo\ "usage:\ $0\ <cache\-drive>\ <backing\-pool>\ <days\-old>"
|
|
\ \ exit\ 1
|
|
fi
|
|
|
|
CACHE="${1}"
|
|
BACKING="${2}"
|
|
N=${3}
|
|
|
|
find\ "${CACHE}"\ \-type\ f\ \-atime\ +${N}\ \-printf\ \[aq]%P\\n\[aq]\ |\ \\
|
|
\ \ rsync\ \-\-files\-from=\-\ \-axqHAXWES\ \-\-preallocate\ \-\-remove\-source\-files\ "${CACHE}/"\ "${BACKING}/"
|
|
\f[]
|
|
.fi
|
|
.SS percentage full expiring
|
|
.PP
|
|
Move the oldest file from the cache to the backing pool.
|
|
Continue till below percentage threshold.
|
|
.PP
|
|
\f[I]NOTE:\f[] The arguments to these scripts include the cache
|
|
\f[B]drive\f[].
|
|
Not the pool with the cache drive.
|
|
You could have data loss if the source is the cache pool.
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
#!/bin/bash
|
|
|
|
if\ [\ $#\ !=\ 3\ ];\ then
|
|
\ \ echo\ "usage:\ $0\ <cache\-drive>\ <backing\-pool>\ <percentage>"
|
|
\ \ exit\ 1
|
|
fi
|
|
|
|
CACHE="${1}"
|
|
BACKING="${2}"
|
|
PERCENTAGE=${3}
|
|
|
|
set\ \-o\ errexit
|
|
while\ [\ $(df\ \-\-output=pcent\ "${CACHE}"\ |\ grep\ \-v\ Use\ |\ cut\ \-d\[aq]%\[aq]\ \-f1)\ \-gt\ ${PERCENTAGE}\ ]
|
|
do
|
|
\ \ \ \ FILE=$(find\ "${CACHE}"\ \-type\ f\ \-printf\ \[aq]%A\@\ %P\\n\[aq]\ |\ \\
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ sort\ |\ \\
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ head\ \-n\ 1\ |\ \\
|
|
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ cut\ \-d\[aq]\ \[aq]\ \-f2\-)
|
|
\ \ \ \ test\ \-n\ "${FILE}"
|
|
\ \ \ \ rsync\ \-axqHAXWESR\ \-\-preallocate\ \-\-remove\-source\-files\ "${CACHE}/./${FILE}"\ "${BACKING}/"
|
|
done
|
|
\f[]
|
|
.fi
|
|
.SH PERFORMANCE
|
|
.PP
|
|
mergerfs is at its core just a proxy and therefore its theoretical max
|
|
performance is that of the underlying devices.
|
|
However, given it is a FUSE filesystem working from userspace there is
|
|
an increase in overhead relative to kernel based solutions.
|
|
That said the performance can match the theoretical max but it depends
|
|
greatly on the system\[aq]s configuration.
|
|
Especially when adding network filesystems into the mix there are many
|
|
variables which can impact performance.
|
|
Drive speeds and latency, network speeds and latency, general
|
|
concurrency, read/write sizes, etc.
|
|
Unfortunately, given the number of variables it has been difficult to
|
|
find a single set of settings which provide optimal performance.
|
|
If you\[aq]re having performance issues please look over the suggestions
|
|
below (including the benchmarking section.)
|
|
.PP
|
|
NOTE: be sure to read about these features before changing them to
|
|
understand what behaviors it may impact
|
|
.IP \[bu] 2
|
|
enable (or disable) \f[C]splice_move\f[], \f[C]splice_read\f[], and
|
|
\f[C]splice_write\f[]
|
|
.IP \[bu] 2
|
|
disable \f[C]security_capability\f[] and/or \f[C]xattr\f[]
|
|
.IP \[bu] 2
|
|
increase cache timeouts \f[C]cache.attr\f[], \f[C]cache.entry\f[],
|
|
\f[C]cache.negative_entry\f[]
|
|
.IP \[bu] 2
|
|
enable (or disable) page caching (\f[C]cache.files\f[])
|
|
.IP \[bu] 2
|
|
enable \f[C]cache.writeback\f[]
|
|
.IP \[bu] 2
|
|
enable \f[C]cache.open\f[]
|
|
.IP \[bu] 2
|
|
enable \f[C]cache.statfs\f[]
|
|
.IP \[bu] 2
|
|
enable \f[C]cache.symlinks\f[]
|
|
.IP \[bu] 2
|
|
enable \f[C]cache.readdir\f[]
|
|
.IP \[bu] 2
|
|
change the number of worker threads
|
|
.IP \[bu] 2
|
|
disable \f[C]posix_acl\f[]
|
|
.IP \[bu] 2
|
|
disable \f[C]async_read\f[]
|
|
.IP \[bu] 2
|
|
test theoretical performance using \f[C]nullrw\f[] or mounting a ram
|
|
disk
|
|
.IP \[bu] 2
|
|
use \f[C]symlinkify\f[] if your data is largely static and read\-only
|
|
.IP \[bu] 2
|
|
use tiered cache drives
|
|
.IP \[bu] 2
|
|
use LVM and LVM cache to place a SSD in front of your HDDs
|
|
.PP
|
|
If you come across a setting that significantly impacts performance
|
|
please contact trapexit so he may investigate further.
|
|
.SH BENCHMARKING
|
|
.PP
|
|
Filesystems are complicated.
|
|
They do many things and many of those are interconnected.
|
|
Additionally, the OS, drivers, hardware, etc.
|
|
all can impact performance.
|
|
Therefore, when benchmarking, it is \f[B]necessary\f[] that the test
|
|
focus as narrowly as possible.
|
|
.PP
|
|
For most throughput is the key benchmark.
|
|
To test throughput \f[C]dd\f[] is useful but \f[B]must\f[] be used with
|
|
the correct settings in order to ensure the filesystem or device is
|
|
actually being tested.
|
|
The OS can and will cache data.
|
|
Without forcing synchronous reads and writes and/or disabling caching
|
|
the values returned will not be representative of the device\[aq]s true
|
|
performance.
|
|
.PP
|
|
When benchmarking through mergerfs ensure you only use 1 branch to
|
|
remove any possibility of the policies complicating the situation.
|
|
Benchmark the underlying filesystem first and then mount mergerfs over
|
|
it and test again.
|
|
If you\[aq]re experience speeds below your expectation you will need to
|
|
narrow down precisely which component is leading to the slowdown.
|
|
Preferably test the following in the order listed (but not combined).
|
|
.IP "1." 3
|
|
Enable \f[C]nullrw\f[] mode with \f[C]nullrw=true\f[].
|
|
This will effectively make reads and writes no\-ops.
|
|
Removing the underlying device / filesystem from the equation.
|
|
This will give us the top theoretical speeds.
|
|
.IP "2." 3
|
|
Mount mergerfs over \f[C]tmpfs\f[].
|
|
\f[C]tmpfs\f[] is a RAM disk.
|
|
Extremely high speed and very low latency.
|
|
This is a more realistic best case scenario.
|
|
Example: \f[C]mount\ \-t\ tmpfs\ \-o\ size=2G\ tmpfs\ /tmp/tmpfs\f[]
|
|
.IP "3." 3
|
|
Mount mergerfs over a local drive.
|
|
NVMe, SSD, HDD, etc.
|
|
If you have more than one I\[aq]d suggest testing each of them as drives
|
|
and/or controllers (their drivers) could impact performance.
|
|
.IP "4." 3
|
|
Finally, if you intend to use mergerfs with a network filesystem, either
|
|
as the source of data or to combine with another through mergerfs, test
|
|
each of those alone as above.
|
|
.PP
|
|
Once you find the component which has the performance issue you can do
|
|
further testing with different options to see if they impact
|
|
performance.
|
|
For reads and writes the most relevant would be: \f[C]cache.files\f[],
|
|
\f[C]async_read\f[], \f[C]splice_move\f[], \f[C]splice_read\f[],
|
|
\f[C]splice_write\f[].
|
|
Less likely but relevant when using NFS or with certain filesystems
|
|
would be \f[C]security_capability\f[], \f[C]xattr\f[], and
|
|
\f[C]posix_acl\f[].
|
|
If you find a specific system, drive, filesystem, controller, etc.
|
|
that performs poorly contact trapexit so he may investigate further.
|
|
.PP
|
|
Sometimes the problem is really the application accessing or writing
|
|
data through mergerfs.
|
|
Some software use small buffer sizes which can lead to more requests and
|
|
therefore greater overhead.
|
|
You can test this out yourself by replace \f[C]bs=1M\f[] in the examples
|
|
below with \f[C]ibs\f[] or \f[C]obs\f[] and using a size of \f[C]512\f[]
|
|
instead of \f[C]1M\f[].
|
|
In one example test using \f[C]nullrw\f[] the write speed dropped from
|
|
4.9GB/s to 69.7MB/s when moving from \f[C]1M\f[] to \f[C]512\f[].
|
|
Similar results were had when testing reads.
|
|
Small writes overhead may be improved by leveraging a write cache but in
|
|
casual tests little gain was found.
|
|
More tests will need to be done before this feature would become
|
|
available.
|
|
If you have an app that appears slow with mergerfs it could be due to
|
|
this.
|
|
Contact trapexit so he may investigate further.
|
|
.SS write benchmark
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ dd\ if=/dev/zero\ of=/mnt/mergerfs/1GB.file\ bs=1M\ count=1024\ oflag=nocache\ conv=fdatasync\ status=progress
|
|
\f[]
|
|
.fi
|
|
.SS read benchmark
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ dd\ if=/mnt/mergerfs/1GB.file\ of=/dev/null\ bs=1M\ count=1024\ iflag=nocache\ conv=fdatasync\ status=progress
|
|
\f[]
|
|
.fi
|
|
.SH TIPS / NOTES
|
|
.IP \[bu] 2
|
|
This document is very literal and thorough.
|
|
Unless there is a bug things work as described.
|
|
If a suspected feature isn\[aq]t mentioned it doesn\[aq]t exist.
|
|
.IP \[bu] 2
|
|
Ensure you\[aq]re using the latest version.
|
|
Few distros have the latest version.
|
|
.IP \[bu] 2
|
|
\f[B]use_ino\f[] will only work when used with mergerfs 2.18.0 and
|
|
above.
|
|
.IP \[bu] 2
|
|
Run mergerfs as \f[C]root\f[] (with \f[B]allow_other\f[]) unless
|
|
you\[aq]re merging paths which are owned by the same user otherwise
|
|
strange permission issues may arise.
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/backup\-and\-recovery\-howtos : A set of
|
|
guides / howtos on creating a data storage system, backing it up,
|
|
maintaining it, and recovering from failure.
|
|
.IP \[bu] 2
|
|
If you don\[aq]t see some directories and files you expect in a merged
|
|
point or policies seem to skip drives be sure the user has permission to
|
|
all the underlying directories.
|
|
Use \f[C]mergerfs.fsck\f[] to audit the drive for out of sync
|
|
permissions.
|
|
.IP \[bu] 2
|
|
Do \f[B]not\f[] use \f[C]cache.files=off\f[] if you expect applications
|
|
(such as rtorrent) to use mmap (http://linux.die.net/man/2/mmap) files.
|
|
Shared mmap is not currently supported in FUSE w/ page caching disabled.
|
|
Enabling \f[C]dropcacheonclose\f[] is recommended when
|
|
\f[C]cache.files=partial|full|auto\-full\f[].
|
|
.IP \[bu] 2
|
|
Kodi (http://kodi.tv), Plex (http://plex.tv),
|
|
Subsonic (http://subsonic.org), etc.
|
|
can use directory mtime (http://linux.die.net/man/2/stat) to more
|
|
efficiently determine whether to scan for new content rather than simply
|
|
performing a full scan.
|
|
If using the default \f[B]getattr\f[] policy of \f[B]ff\f[] it\[aq]s
|
|
possible those programs will miss an update on account of it returning
|
|
the first directory found\[aq]s \f[B]stat\f[] info and its a later
|
|
directory on another mount which had the \f[B]mtime\f[] recently
|
|
updated.
|
|
To fix this you will want to set \f[B]func.getattr=newest\f[].
|
|
Remember though that this is just \f[B]stat\f[].
|
|
If the file is later \f[B]open\f[]\[aq]ed or \f[B]unlink\f[]\[aq]ed and
|
|
the policy is different for those then a completely different file or
|
|
directory could be acted on.
|
|
.IP \[bu] 2
|
|
Some policies mixed with some functions may result in strange behaviors.
|
|
Not that some of these behaviors and race conditions couldn\[aq]t happen
|
|
outside \f[B]mergerfs\f[] but that they are far more likely to occur on
|
|
account of the attempt to merge together multiple sources of data which
|
|
could be out of sync due to the different policies.
|
|
.IP \[bu] 2
|
|
For consistency its generally best to set \f[B]category\f[] wide
|
|
policies rather than individual \f[B]func\f[]\[aq]s.
|
|
This will help limit the confusion of tools such as
|
|
rsync (http://linux.die.net/man/1/rsync).
|
|
However, the flexibility is there if needed.
|
|
.SH KNOWN ISSUES / BUGS
|
|
.SS kernel issues & bugs
|
|
.PP
|
|
<https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs>
|
|
.SS directory mtime is not being updated
|
|
.PP
|
|
Remember that the default policy for \f[C]getattr\f[] is \f[C]ff\f[].
|
|
The information for the first directory found will be returned.
|
|
If it wasn\[aq]t the directory which had been updated then it will
|
|
appear outdated.
|
|
.PP
|
|
The reason this is the default is because any other policy would be more
|
|
expensive and for many applications it is unnecessary.
|
|
To always return the directory with the most recent mtime or a faked
|
|
value based on all found would require a scan of all drives.
|
|
.PP
|
|
If you always want the directory information from the one with the most
|
|
recent mtime then use the \f[C]newest\f[] policy for \f[C]getattr\f[].
|
|
.SS \[aq]mv /mnt/pool/foo /mnt/disk1/foo\[aq] removes \[aq]foo\[aq]
|
|
.PP
|
|
This is not a bug.
|
|
.PP
|
|
Run in verbose mode to better understand what\[aq]s happening:
|
|
.IP
|
|
.nf
|
|
\f[C]
|
|
$\ mv\ \-v\ /mnt/pool/foo\ /mnt/disk1/foo
|
|
copied\ \[aq]/mnt/pool/foo\[aq]\ \->\ \[aq]/mnt/disk1/foo\[aq]
|
|
removed\ \[aq]/mnt/pool/foo\[aq]
|
|
$\ ls\ /mnt/pool/foo
|
|
ls:\ cannot\ access\ \[aq]/mnt/pool/foo\[aq]:\ No\ such\ file\ or\ directory
|
|
\f[]
|
|
.fi
|
|
.PP
|
|
\f[C]mv\f[], when working across devices, is copying the source to
|
|
target and then removing the source.
|
|
Since the source \f[B]is\f[] the target in this case, depending on the
|
|
unlink policy, it will remove the just copied file and other files
|
|
across the branches.
|
|
.PP
|
|
If you want to move files to one drive just copy them there and use
|
|
mergerfs.dedup to clean up the old paths or manually remove them from
|
|
the branches directly.
|
|
.SS cached memory appears greater than it should be
|
|
.PP
|
|
Use \f[C]cache.files=off\f[] and/or \f[C]dropcacheonclose=true\f[].
|
|
See the section on page caching.
|
|
.SS NFS clients returning ESTALE / Stale file handle
|
|
.PP
|
|
NFS does not like out of band changes.
|
|
That is especially true of inode values.
|
|
.PP
|
|
Be sure to use the following options:
|
|
.IP \[bu] 2
|
|
noforget
|
|
.IP \[bu] 2
|
|
use_ino
|
|
.IP \[bu] 2
|
|
inodecalc=path\-hash
|
|
.SS rtorrent fails with ENODEV (No such device)
|
|
.PP
|
|
Be sure to set \f[C]cache.files=partial|full|auto\-full\f[] or turn off
|
|
\f[C]direct_io\f[].
|
|
rtorrent and some other applications use
|
|
mmap (http://linux.die.net/man/2/mmap) to read and write to files and
|
|
offer no fallback to traditional methods.
|
|
FUSE does not currently support mmap while using \f[C]direct_io\f[].
|
|
There may be a performance penalty on writes with \f[C]direct_io\f[] off
|
|
as well as the problem of double caching but it\[aq]s the only way to
|
|
get such applications to work.
|
|
If the performance loss is too high for other apps you can mount
|
|
mergerfs twice.
|
|
Once with \f[C]direct_io\f[] enabled and one without it.
|
|
Be sure to set \f[C]dropcacheonclose=true\f[] if not using
|
|
\f[C]direct_io\f[].
|
|
.SS Plex doesn\[aq]t work with mergerfs
|
|
.PP
|
|
It does.
|
|
If you\[aq]re trying to put Plex\[aq]s config / metadata / database on
|
|
mergerfs you can\[aq]t set \f[C]cache.files=off\f[] because Plex is
|
|
using sqlite3 with mmap enabled.
|
|
Shared mmap is not supported by Linux\[aq]s FUSE implementation when
|
|
page caching is disabled.
|
|
To fix this place the data elsewhere (preferable) or enable
|
|
\f[C]cache.files\f[] (with \f[C]dropcacheonclose=true\f[]).
|
|
Sqlite3 does not need mmap but the developer needs to fall back to
|
|
standard IO if mmap fails.
|
|
.PP
|
|
If the issue is that scanning doesn\[aq]t seem to pick up media then be
|
|
sure to set \f[C]func.getattr=newest\f[] though generally a full scan
|
|
will pick up all media anyway.
|
|
.SS When a program tries to move or rename a file it fails
|
|
.PP
|
|
Please read the section above regarding rename & link (#rename--link).
|
|
.PP
|
|
The problem is that many applications do not properly handle
|
|
\f[C]EXDEV\f[] errors which \f[C]rename\f[] and \f[C]link\f[] may return
|
|
even though they are perfectly valid situations which do not indicate
|
|
actual drive or OS errors.
|
|
The error will only be returned by mergerfs if using a path preserving
|
|
policy as described in the policy section above.
|
|
If you do not care about path preservation simply change the mergerfs
|
|
policy to the non\-path preserving version.
|
|
For example: \f[C]\-o\ category.create=mfs\f[]
|
|
.PP
|
|
Ideally the offending software would be fixed and it is recommended that
|
|
if you run into this problem you contact the software\[aq]s author and
|
|
request proper handling of \f[C]EXDEV\f[] errors.
|
|
.SS my 32bit software has problems
|
|
.PP
|
|
Some software have problems with 64bit inode values.
|
|
The symptoms can include EOVERFLOW errors when trying to list files.
|
|
You can address this by setting \f[C]inodecalc\f[] to one of the 32bit
|
|
based algos as described in the relevant section.
|
|
.SS Samba: Moving files / directories fails
|
|
.PP
|
|
Workaround: Copy the file/directory and then remove the original rather
|
|
than move.
|
|
.PP
|
|
This isn\[aq]t an issue with Samba but some SMB clients.
|
|
GVFS\-fuse v1.20.3 and prior (found in Ubuntu 14.04 among others) failed
|
|
to handle certain error codes correctly.
|
|
Particularly \f[B]STATUS_NOT_SAME_DEVICE\f[] which comes from the
|
|
\f[B]EXDEV\f[] which is returned by \f[B]rename\f[] when the call is
|
|
crossing mount points.
|
|
When a program gets an \f[B]EXDEV\f[] it needs to explicitly take an
|
|
alternate action to accomplish its goal.
|
|
In the case of \f[B]mv\f[] or similar it tries \f[B]rename\f[] and on
|
|
\f[B]EXDEV\f[] falls back to a manual copying of data between the two
|
|
locations and unlinking the source.
|
|
In these older versions of GVFS\-fuse if it received \f[B]EXDEV\f[] it
|
|
would translate that into \f[B]EIO\f[].
|
|
This would cause \f[B]mv\f[] or most any application attempting to move
|
|
files around on that SMB share to fail with a IO error.
|
|
.PP
|
|
GVFS\-fuse v1.22.0 (https://bugzilla.gnome.org/show_bug.cgi?id=734568)
|
|
and above fixed this issue but a large number of systems use the older
|
|
release.
|
|
On Ubuntu the version can be checked by issuing
|
|
\f[C]apt\-cache\ showpkg\ gvfs\-fuse\f[].
|
|
Most distros released in 2015 seem to have the updated release and will
|
|
work fine but older systems may not.
|
|
Upgrading gvfs\-fuse or the distro in general will address the problem.
|
|
.PP
|
|
In Apple\[aq]s MacOSX 10.9 they replaced Samba (client and server) with
|
|
their own product.
|
|
It appears their new client does not handle \f[B]EXDEV\f[] either and
|
|
responds similar to older release of gvfs on Linux.
|
|
.SS Trashing files occasionally fails
|
|
.PP
|
|
This is the same issue as with Samba.
|
|
\f[C]rename\f[] returns \f[C]EXDEV\f[] (in our case that will really
|
|
only happen with path preserving policies like \f[C]epmfs\f[]) and the
|
|
software doesn\[aq]t handle the situation well.
|
|
This is unfortunately a common failure of software which moves files
|
|
around.
|
|
The standard indicates that an implementation \f[C]MAY\f[] choose to
|
|
support non\-user home directory trashing of files (which is a
|
|
\f[C]MUST\f[]).
|
|
The implementation \f[C]MAY\f[] also support "top directory trashes"
|
|
which many probably do.
|
|
.PP
|
|
To create a \f[C]$topdir/.Trash\f[] directory as defined in the standard
|
|
use the mergerfs\-tools (https://github.com/trapexit/mergerfs-tools)
|
|
tool \f[C]mergerfs.mktrash\f[].
|
|
.SS tar: Directory renamed before its status could be extracted
|
|
.PP
|
|
Make sure to use the \f[C]use_ino\f[] option.
|
|
.SS Supplemental user groups
|
|
.PP
|
|
Due to the overhead of
|
|
getgroups/setgroups (http://linux.die.net/man/2/setgroups) mergerfs
|
|
utilizes a cache.
|
|
This cache is opportunistic and per thread.
|
|
Each thread will query the supplemental groups for a user when that
|
|
particular thread needs to change credentials and will keep that data
|
|
for the lifetime of the thread.
|
|
This means that if a user is added to a group it may not be picked up
|
|
without the restart of mergerfs.
|
|
However, since the high level FUSE API\[aq]s (at least the standard
|
|
version) thread pool dynamically grows and shrinks it\[aq]s possible
|
|
that over time a thread will be killed and later a new thread with no
|
|
cache will start and query the new data.
|
|
.PP
|
|
The gid cache uses fixed storage to simplify the design and be
|
|
compatible with older systems which may not have C++11 compilers.
|
|
There is enough storage for 256 users\[aq] supplemental groups.
|
|
Each user is allowed up to 32 supplemental groups.
|
|
Linux >= 2.6.3 allows up to 65535 groups per user but most other *nixs
|
|
allow far less.
|
|
NFS allowing only 16.
|
|
The system does handle overflow gracefully.
|
|
If the user has more than 32 supplemental groups only the first 32 will
|
|
be used.
|
|
If more than 256 users are using the system when an uncached user is
|
|
found it will evict an existing user\[aq]s cache at random.
|
|
So long as there aren\[aq]t more than 256 active users this should be
|
|
fine.
|
|
If either value is too low for your needs you will have to modify
|
|
\f[C]gidcache.hpp\f[] to increase the values.
|
|
Note that doing so will increase the memory needed by each thread.
|
|
.PP
|
|
While not a bug some users have found when using containers that
|
|
supplemental groups defined inside the container don\[aq]t work properly
|
|
with regard to permissions.
|
|
This is expected as mergerfs lives outside the container and therefore
|
|
is querying the host\[aq]s group database.
|
|
There might be a hack to work around this (make mergerfs read the
|
|
/etc/group file in the container) but it is not yet implemented and
|
|
would be limited to Linux and the /etc/group DB.
|
|
Preferably users would mount in the host group file into the containers
|
|
or use a standard shared user & groups technology like NIS or LDAP.
|
|
.SS mergerfs or libfuse crashing
|
|
.PP
|
|
First...
|
|
always upgrade to the latest version unless told otherwise.
|
|
.PP
|
|
If using mergerfs below 2.22.0:
|
|
.PP
|
|
If suddenly the mergerfs mount point disappears and
|
|
\f[C]Transport\ endpoint\ is\ not\ connected\f[] is returned when
|
|
attempting to perform actions within the mount directory \f[B]and\f[]
|
|
the version of libfuse (use \f[C]mergerfs\ \-v\f[] to find the version)
|
|
is older than \f[C]2.9.4\f[] its likely due to a bug in libfuse.
|
|
Affected versions of libfuse can be found in Debian Wheezy, Ubuntu
|
|
Precise and others.
|
|
.PP
|
|
In order to fix this please install newer versions of libfuse.
|
|
If using a Debian based distro (Debian,Ubuntu,Mint) you can likely just
|
|
install newer versions of
|
|
libfuse (https://packages.debian.org/unstable/libfuse2) and
|
|
fuse (https://packages.debian.org/unstable/fuse) from the repo of a
|
|
newer release.
|
|
.PP
|
|
If using mergerfs at or above 2.22.0:
|
|
.PP
|
|
First upgrade if possible, check the known bugs section, and contact
|
|
trapexit.
|
|
.SS mergerfs appears to be crashing or exiting
|
|
.PP
|
|
There seems to be an issue with Linux version \f[C]4.9.0\f[] and above
|
|
in which an invalid message appears to be transmitted to libfuse (used
|
|
by mergerfs) causing it to exit.
|
|
No messages will be printed in any logs as it\[aq]s not a proper crash.
|
|
Debugging of the issue is still ongoing and can be followed via the
|
|
fuse\-devel
|
|
thread (https://sourceforge.net/p/fuse/mailman/message/35662577).
|
|
.SS rm: fts_read failed: No such file or directory
|
|
.PP
|
|
Please update.
|
|
This is only happened to mergerfs versions at or below v2.25.x and will
|
|
not occur in more recent versions.
|
|
.SH FAQ
|
|
.SS How well does mergerfs scale? Is it "production ready?"
|
|
.PP
|
|
Users have reported running mergerfs on everything from a Raspberry Pi
|
|
to dual socket Xeon systems with >20 cores.
|
|
I\[aq]m aware of at least a few companies which use mergerfs in
|
|
production.
|
|
Open Media Vault (https://www.openmediavault.org) includes mergerfs as
|
|
its sole solution for pooling drives.
|
|
The author of mergerfs had it running for over 300 days managing 16+
|
|
drives with reasonably heavy 24/7 read and write usage.
|
|
Stopping only after the machine\[aq]s power supply died.
|
|
.PP
|
|
Most serious issues (crashes or data corruption) have been due to kernel
|
|
bugs (https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs).
|
|
All of which are fixed in stable releases.
|
|
.SS Can mergerfs be used with drives which already have data / are in
|
|
use?
|
|
.PP
|
|
Yes.
|
|
MergerFS is a proxy and does \f[B]NOT\f[] interfere with the normal form
|
|
or function of the drives / mounts / paths it manages.
|
|
.PP
|
|
MergerFS is \f[B]not\f[] a traditional filesystem.
|
|
MergerFS is \f[B]not\f[] RAID.
|
|
It does \f[B]not\f[] manipulate the data that passes through it.
|
|
It does \f[B]not\f[] shard data across drives.
|
|
It merely shards some \f[B]behavior\f[] and aggregates others.
|
|
.SS Can mergerfs be removed without affecting the data?
|
|
.PP
|
|
See the previous question\[aq]s answer.
|
|
.SS What policies should I use?
|
|
.PP
|
|
Unless you\[aq]re doing something more niche the average user is
|
|
probably best off using \f[C]mfs\f[] for \f[C]category.create\f[].
|
|
It will spread files out across your branches based on available space.
|
|
Use \f[C]mspmfs\f[] if you want to try to colocate the data a bit more.
|
|
You may want to use \f[C]lus\f[] if you prefer a slightly different
|
|
distribution of data if you have a mix of smaller and larger drives.
|
|
Generally though \f[C]mfs\f[], \f[C]lus\f[], or even \f[C]rand\f[] are
|
|
good for the general use case.
|
|
If you are starting with an imbalanced pool you can use the tool
|
|
\f[B]mergerfs.balance\f[] to redistribute files across the pool.
|
|
.PP
|
|
If you really wish to try to colocate files based on directory you can
|
|
set \f[C]func.create\f[] to \f[C]epmfs\f[] or similar and
|
|
\f[C]func.mkdir\f[] to \f[C]rand\f[] or \f[C]eprand\f[] depending on if
|
|
you just want to colocate generally or on specific branches.
|
|
Either way the \f[I]need\f[] to colocate is rare.
|
|
For instance: if you wish to remove the drive regularly and want the
|
|
data to predictably be on that drive or if you don\[aq]t use backup at
|
|
all and don\[aq]t wish to replace that data piecemeal.
|
|
In which case using path preservation can help but will require some
|
|
manual attention.
|
|
Colocating after the fact can be accomplished using the
|
|
\f[B]mergerfs.consolidate\f[] tool.
|
|
If you don\[aq]t need strict colocation which the \f[C]ep\f[] policies
|
|
provide then you can use the \f[C]msp\f[] based policies which will walk
|
|
back the path till finding a branch that works.
|
|
.PP
|
|
Ultimately there is no correct answer.
|
|
It is a preference or based on some particular need.
|
|
mergerfs is very easy to test and experiment with.
|
|
I suggest creating a test setup and experimenting to get a sense of what
|
|
you want.
|
|
.PP
|
|
The reason \f[C]mfs\f[] is not the default \f[C]category.create\f[]
|
|
policy is historical.
|
|
When/if a 3.X gets released it will be changed to minimize confusion
|
|
people often have with path preserving policies.
|
|
.SS What settings should I use?
|
|
.PP
|
|
Depends on what features you want.
|
|
Generally speaking there are no "wrong" settings.
|
|
All settings are performance or feature related.
|
|
The best bet is to read over the available options and choose what fits
|
|
your situation.
|
|
If something isn\[aq]t clear from the documentation please reach out and
|
|
the documentation will be improved.
|
|
.PP
|
|
That said, for the average person, the following should be fine:
|
|
.PP
|
|
\f[C]\-o\ use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs\f[]
|
|
.SS Why are all my files ending up on 1 drive?!
|
|
.PP
|
|
Did you start with empty drives?
|
|
Did you explicitly configure a \f[C]category.create\f[] policy?
|
|
Are you using an \f[C]existing\ path\f[] / \f[C]path\ preserving\f[]
|
|
policy?
|
|
.PP
|
|
The default create policy is \f[C]epmfs\f[].
|
|
That is a path preserving algorithm.
|
|
With such a policy for \f[C]mkdir\f[] and \f[C]create\f[] with a set of
|
|
empty drives it will select only 1 drive when the first directory is
|
|
created.
|
|
Anything, files or directories, created in that first directory will be
|
|
placed on the same branch because it is preserving paths.
|
|
.PP
|
|
This catches a lot of new users off guard but changing the default would
|
|
break the setup for many existing users.
|
|
If you do not care about path preservation and wish your files to be
|
|
spread across all your drives change to \f[C]mfs\f[] or similar policy
|
|
as described above.
|
|
If you do want path preservation you\[aq]ll need to perform the manual
|
|
act of creating paths on the drives you want the data to land on before
|
|
transferring your data.
|
|
Setting \f[C]func.mkdir=epall\f[] can simplify managing path
|
|
preservation for \f[C]create\f[].
|
|
Or use \f[C]func.mkdir=rand\f[] if you\[aq]re interested in just
|
|
grouping together directory content by drive.
|
|
.SS Do hardlinks work?
|
|
.PP
|
|
Yes.
|
|
You need to use \f[C]use_ino\f[] to support proper reporting of inodes
|
|
but they work regardless.
|
|
See also the option \f[C]inodecalc\f[].
|
|
.PP
|
|
What mergerfs does not do is fake hard links across branches.
|
|
Read the section "rename & link" for how it works.
|
|
.PP
|
|
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 between them.
|
|
You should mount in the highest directory in the mergerfs pool that
|
|
includes all the paths you need if you want links to work.
|
|
.SS Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
|
.PP
|
|
Yes.
|
|
They are completely unreleated pieces of software.
|
|
.SS Does mergerfs support CoW / copy\-on\-write / writes to read\-only
|
|
filesystems?
|
|
.PP
|
|
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs
|
|
or aufs sense.
|
|
It does offer a
|
|
cow\-shell (http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html)
|
|
like hard link breaking (copy to temp file then rename over original)
|
|
which can be useful when wanting to save space by hardlinking duplicate
|
|
files but wish to treat each name as if it were a unique and separate
|
|
file.
|
|
.PP
|
|
If you want to write to a read\-only filesystem you should look at
|
|
overlayfs.
|
|
You can always include the overlayfs mount into a mergerfs pool.
|
|
.SS Why can\[aq]t I see my files / directories?
|
|
.PP
|
|
It\[aq]s almost always a permissions issue.
|
|
Unlike mhddfs and unionfs\-fuse, which runs as root and attempts to
|
|
access content as such, mergerfs always changes its credentials to that
|
|
of the caller.
|
|
This means that if the user does not have access to a file or directory
|
|
than neither will mergerfs.
|
|
However, because mergerfs is creating a union of paths it may be able to
|
|
read some files and directories on one drive but not another resulting
|
|
in an incomplete set.
|
|
.PP
|
|
Whenever you run into a split permission issue (seeing some but not all
|
|
files) try using
|
|
mergerfs.fsck (https://github.com/trapexit/mergerfs-tools) tool to check
|
|
for and fix the mismatch.
|
|
If you aren\[aq]t seeing anything at all be sure that the basic
|
|
permissions are correct.
|
|
The user and group values are correct and that directories have their
|
|
executable bit set.
|
|
A common mistake by users new to Linux is to \f[C]chmod\ \-R\ 644\f[]
|
|
when they should have \f[C]chmod\ \-R\ u=rwX,go=rX\f[].
|
|
.PP
|
|
If using a network filesystem such as NFS, SMB, CIFS (Samba) be sure to
|
|
pay close attention to anything regarding permissioning and users.
|
|
Root squashing and user translation for instance has bitten a few
|
|
mergerfs users.
|
|
Some of these also affect the use of mergerfs from container platforms
|
|
such as Docker.
|
|
.SS Is my OS\[aq]s libfuse needed for mergerfs to work?
|
|
.PP
|
|
No.
|
|
Normally \f[C]mount.fuse\f[] is needed to get mergerfs (or any FUSE
|
|
filesystem to mount using the \f[C]mount\f[] command but in vendoring
|
|
the libfuse library the \f[C]mount.fuse\f[] app has been renamed to
|
|
\f[C]mount.mergerfs\f[] meaning the filesystem type in \f[C]fstab\f[]
|
|
can simply be \f[C]mergerfs\f[].
|
|
That said there should be no harm in having it installed and continuing
|
|
to using \f[C]fuse.mergerfs\f[] as the type in \f[C]/etc/fstab\f[].
|
|
.PP
|
|
If \f[C]mergerfs\f[] doesn\[aq]t work as a type it could be due to how
|
|
the \f[C]mount.mergerfs\f[] tool was installed.
|
|
Must be in \f[C]/sbin/\f[] with proper permissions.
|
|
.SS Why was libfuse embedded into mergerfs?
|
|
.IP "1." 3
|
|
A significant number of users use mergerfs on distros with old versions
|
|
of libfuse which have serious bugs.
|
|
Requiring updated versions of libfuse on those distros isn\[aq]t
|
|
practical (no package offered, user inexperience, etc.).
|
|
The only practical way to provide a stable runtime on those systems was
|
|
to "vendor" / embed the library into the project.
|
|
.IP "2." 3
|
|
mergerfs was written to use the high level API.
|
|
There are a number of limitations in the HLAPI that make certain
|
|
features difficult or impossible to implement.
|
|
While some of these features could be patched into newer versions of
|
|
libfuse without breaking the public API some of them would require hacky
|
|
code to provide backwards compatibility.
|
|
While it may still be worth working with upstream to address these
|
|
issues in future versions, since the library needs to be vendored for
|
|
stability and compatibility reasons it is preferable / easier to modify
|
|
the API.
|
|
Longer term the plan is to rewrite mergerfs to use the low level API.
|
|
.SS Why did support for system libfuse get removed?
|
|
.PP
|
|
See above first.
|
|
.PP
|
|
If/when mergerfs is rewritten to use the low\-level API then it\[aq]ll
|
|
be plausible to support system libfuse but till then its simply too much
|
|
work to manage the differences across the versions.
|
|
.SS Why use mergerfs over mhddfs?
|
|
.PP
|
|
mhddfs is no longer maintained and has some known stability and security
|
|
issues (see below).
|
|
MergerFS provides a superset of mhddfs\[aq] features and should offer
|
|
the same or maybe better performance.
|
|
.PP
|
|
Below is an example of mhddfs and mergerfs setup to work similarly.
|
|
.PP
|
|
\f[C]mhddfs\ \-o\ mlimit=4G,allow_other\ /mnt/drive1,/mnt/drive2\ /mnt/pool\f[]
|
|
.PP
|
|
\f[C]mergerfs\ \-o\ minfreespace=4G,allow_other,category.create=ff\ /mnt/drive1:/mnt/drive2\ /mnt/pool\f[]
|
|
.SS Why use mergerfs over aufs?
|
|
.PP
|
|
aufs is mostly abandoned and no longer available in many distros.
|
|
.PP
|
|
While aufs can offer better peak performance mergerfs provides more
|
|
configurability and is generally easier to use.
|
|
mergerfs however does not offer the overlay / copy\-on\-write (CoW)
|
|
features which aufs and overlayfs have.
|
|
.SS Why use mergerfs over unionfs?
|
|
.PP
|
|
UnionFS is more like aufs than mergerfs in that it offers overlay / CoW
|
|
features.
|
|
If you\[aq]re just looking to create a union of drives and want
|
|
flexibility in file/directory placement then mergerfs offers that
|
|
whereas unionfs is more for overlaying RW filesystems over RO ones.
|
|
.SS Why use mergerfs over overlayfs?
|
|
.PP
|
|
Same reasons as with unionfs.
|
|
.SS Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation /
|
|
striping?
|
|
.PP
|
|
With simple JBOD / drive concatenation / stripping / RAID0 a single
|
|
drive failure will result in full pool failure.
|
|
mergerfs performs a similar function without the possibility of
|
|
catastrophic failure and the difficulties in recovery.
|
|
Drives may fail, however, all other data will continue to be accessible.
|
|
.PP
|
|
When combined with something like SnapRaid (http://www.snapraid.it)
|
|
and/or an offsite backup solution you can have the flexibility of JBOD
|
|
without the single point of failure.
|
|
.SS Why use mergerfs over ZFS?
|
|
.PP
|
|
MergerFS is not intended to be a replacement for ZFS.
|
|
MergerFS is intended to provide flexible pooling of arbitrary drives
|
|
(local or remote), of arbitrary sizes, and arbitrary filesystems.
|
|
For \f[C]write\ once,\ read\ many\f[] usecases such as bulk media
|
|
storage.
|
|
Where data integrity and backup is managed in other ways.
|
|
In that situation 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).
|
|
.SS Why use mergerfs over UnRAID?
|
|
.PP
|
|
UnRAID is a full OS and its storage layer, as I understand, is
|
|
proprietary and closed source.
|
|
Users who have experience with both have said they prefer the
|
|
flexibility offered by mergerfs and for some the fact it is free and
|
|
open source is important.
|
|
.PP
|
|
There are a number of UnRAID users who use mergerfs as well though
|
|
I\[aq]m not entirely familiar with the use case.
|
|
.SS What should mergerfs NOT be used for?
|
|
.IP \[bu] 2
|
|
databases: Even if the database stored data in separate files (mergerfs
|
|
wouldn\[aq]t offer much otherwise) the higher latency of the indirection
|
|
will kill performance.
|
|
If it is a lightly used SQLITE database then it may be fine but
|
|
you\[aq]ll need to test.
|
|
.IP \[bu] 2
|
|
VM images: For the same reasons as databases.
|
|
VM images are accessed very aggressively and mergerfs will introduce too
|
|
much latency (if it works at all).
|
|
.IP \[bu] 2
|
|
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.
|
|
.SS Can drives be written to directly? Outside of mergerfs while pooled?
|
|
.PP
|
|
Yes, however it\[aq]s not recommended to use the same file from within
|
|
the pool and from without at the same time (particularly writing).
|
|
Especially if using caching of any kind (cache.files, cache.entry,
|
|
cache.attr, cache.negative_entry, cache.symlinks, cache.readdir, etc.)
|
|
as there could be a conflict between cached data and not.
|
|
.SS Why do I get an "out of space" / "no space left on device" / ENOSPC
|
|
error even though there appears to be lots of space available?
|
|
.PP
|
|
First make sure you\[aq]ve read the sections above about policies, path
|
|
preservation, branch filtering, and the options \f[B]minfreespace\f[],
|
|
\f[B]moveonenospc\f[], \f[B]statfs\f[], and \f[B]statfs_ignore\f[].
|
|
.PP
|
|
mergerfs is simply presenting a union of the content within multiple
|
|
branches.
|
|
The reported free space is an aggregate of space available within the
|
|
pool (behavior modified by \f[B]statfs\f[] and \f[B]statfs_ignore\f[]).
|
|
It does not represent a contiguous space.
|
|
In the same way that read\-only filesystems, those with quotas, or
|
|
reserved space report the full theoretical space available.
|
|
.PP
|
|
Due to path preservation, branch tagging, read\-only status, and
|
|
\f[B]minfreespace\f[] settings it is perfectly valid that
|
|
\f[C]ENOSPC\f[] / "out of space" / "no space left on device" be
|
|
returned.
|
|
It is doing what was asked of it: filtering possible branches due to
|
|
those settings.
|
|
Only one error can be returned and if one of the reasons for filtering a
|
|
branch was \f[B]minfreespace\f[] then it will be returned as such.
|
|
\f[B]moveonenospc\f[] is only relevant to writing a file which is too
|
|
large for the drive its currently on.
|
|
.PP
|
|
It is also possible that the filesystem selected has run out of inodes.
|
|
Use \f[C]df\ \-i\f[] to list the total and available inodes per
|
|
filesystem.
|
|
.PP
|
|
If you don\[aq]t care about path preservation then simply change the
|
|
\f[C]create\f[] policy to one which isn\[aq]t.
|
|
\f[C]mfs\f[] is probably what most are looking for.
|
|
The reason it\[aq]s not default is because it was originally set to
|
|
\f[C]epmfs\f[] and changing it now would change people\[aq]s setup.
|
|
Such a setting change will likely occur in mergerfs 3.
|
|
.SS Why does the total available space in mergerfs not equal outside?
|
|
.PP
|
|
Are you using ext2/3/4?
|
|
With reserve for root?
|
|
mergerfs uses available space for statfs calculations.
|
|
If you\[aq]ve reserved space for root then it won\[aq]t show up.
|
|
.PP
|
|
You can remove the reserve by running:
|
|
\f[C]tune2fs\ \-m\ 0\ <device>\f[]
|
|
.SS Can mergerfs mounts be exported over NFS?
|
|
.PP
|
|
Yes, however if you do anything which may changes files out of band
|
|
(including for example using the \f[C]newest\f[] policy) it will result
|
|
in "stale file handle" errors unless properly setup.
|
|
.PP
|
|
Be sure to use the following options:
|
|
.IP \[bu] 2
|
|
noforget
|
|
.IP \[bu] 2
|
|
use_ino
|
|
.IP \[bu] 2
|
|
inodecalc=path\-hash
|
|
.SS Can mergerfs mounts be exported over Samba / SMB?
|
|
.PP
|
|
Yes.
|
|
While some users have reported problems it appears to always be related
|
|
to how Samba is setup in relation to permissions.
|
|
.SS Can mergerfs mounts be used over SSHFS?
|
|
.PP
|
|
Yes.
|
|
.SS I notice massive slowdowns of writes when enabling cache.files.
|
|
.PP
|
|
When file caching is enabled in any form (\f[C]cache.files!=off\f[] or
|
|
\f[C]direct_io=false\f[]) it will issue \f[C]getxattr\f[] requests for
|
|
\f[C]security.capability\f[] prior to \f[I]every single write\f[].
|
|
This will usually result in a performance degradation, especially when
|
|
using a network filesystem (such as NFS or CIFS/SMB/Samba.)
|
|
Unfortunately at this moment the kernel is not caching the response.
|
|
.PP
|
|
To work around this situation mergerfs offers a few solutions.
|
|
.IP "1." 3
|
|
Set \f[C]security_capability=false\f[].
|
|
It will short circuit any call and return \f[C]ENOATTR\f[].
|
|
This still means though that mergerfs will receive the request before
|
|
every write but at least it doesn\[aq]t get passed through to the
|
|
underlying filesystem.
|
|
.IP "2." 3
|
|
Set \f[C]xattr=noattr\f[].
|
|
Same as above but applies to \f[I]all\f[] calls to getxattr.
|
|
Not just \f[C]security.capability\f[].
|
|
This will not be cached by the kernel either but mergerfs\[aq] runtime
|
|
config system will still function.
|
|
.IP "3." 3
|
|
Set \f[C]xattr=nosys\f[].
|
|
Results in mergerfs returning \f[C]ENOSYS\f[] which \f[I]will\f[] be
|
|
cached by the kernel.
|
|
No future xattr calls will be forwarded to mergerfs.
|
|
The downside is that also means the xattr based config and query
|
|
functionality won\[aq]t work either.
|
|
.IP "4." 3
|
|
Disable file caching.
|
|
If you aren\[aq]t using applications which use \f[C]mmap\f[] it\[aq]s
|
|
probably simpler to just disable it all together.
|
|
The kernel won\[aq]t send the requests when caching is disabled.
|
|
.SS What are these .fuse_hidden files?
|
|
.PP
|
|
Please upgrade.
|
|
mergerfs >= 2.26.0 will not have these temporary files.
|
|
See the notes on \f[C]unlink\f[].
|
|
.SS It\[aq]s mentioned that there are some security issues with mhddfs.
|
|
What are they? How does mergerfs address them?
|
|
.PP
|
|
mhddfs (https://github.com/trapexit/mhddfs) manages running as
|
|
\f[B]root\f[] by calling
|
|
getuid() (https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319)
|
|
and if it returns \f[B]0\f[] then it will
|
|
chown (http://linux.die.net/man/1/chown) the file.
|
|
Not only is that a race condition but it doesn\[aq]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.
|
|
.PP
|
|
In Linux setreuid syscalls apply only to the thread.
|
|
GLIBC hides this away by using realtime signals to inform all threads to
|
|
change credentials.
|
|
Taking after \f[B]Samba\f[], mergerfs uses
|
|
\f[B]syscall(SYS_setreuid,...)\f[] to set the callers credentials for
|
|
that thread only.
|
|
Jumping back to \f[B]root\f[] as necessary should escalated privileges
|
|
be needed (for instance: to clone paths between drives).
|
|
.PP
|
|
For non\-Linux systems mergerfs uses a read\-write lock and changes
|
|
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\[aq]s credentials when it can.
|
|
If the ability to give writers priority is supported then that flag will
|
|
be used so threads trying to change credentials don\[aq]t starve.
|
|
This isn\[aq]t the best solution but should work reasonably well
|
|
assuming there are few users.
|
|
.SH SUPPORT
|
|
.PP
|
|
Filesystems are complex and difficult to debug.
|
|
mergerfs, while being just a proxy of sorts, is also very difficult to
|
|
debug given the large number of possible settings it can have itself and
|
|
the massive number of environments it can run in.
|
|
When reporting on a suspected issue \f[B]please, please\f[] include as
|
|
much of the below information as possible otherwise it will be difficult
|
|
or impossible to diagnose.
|
|
Also please make sure to read all of the above documentation as it
|
|
includes nearly every known system or user issue previously encountered.
|
|
.PP
|
|
\f[B]Please make sure you are using the latest
|
|
release (https://github.com/trapexit/mergerfs/releases) or have tried it
|
|
in comparison. Old versions, which are often included in distros like
|
|
Debian and Ubuntu, are not ever going to be updated and your bug may
|
|
have been addressed already.\f[]
|
|
.SS Information to include in bug reports
|
|
.IP \[bu] 2
|
|
Version of mergerfs: \f[C]mergerfs\ \-V\f[]
|
|
.IP \[bu] 2
|
|
mergerfs settings: from \f[C]/etc/fstab\f[] or command line execution
|
|
.IP \[bu] 2
|
|
Version of Linux: \f[C]uname\ \-a\f[]
|
|
.IP \[bu] 2
|
|
Versions of any additional software being used
|
|
.IP \[bu] 2
|
|
List of drives, their filesystems, and sizes (before and after issue):
|
|
\f[C]df\ \-h\f[]
|
|
.IP \[bu] 2
|
|
\f[B]All\f[] information about the relevant branches and paths:
|
|
permissions, etc.
|
|
.IP \[bu] 2
|
|
A \f[C]strace\f[] of the app having problems:
|
|
.IP \[bu] 2
|
|
\f[C]strace\ \-fvTtt\ \-s\ 256\ \-o\ /tmp/app.strace.txt\ <cmd>\f[]
|
|
.IP \[bu] 2
|
|
A \f[C]strace\f[] of mergerfs while the program is trying to do whatever
|
|
it\[aq]s failing to do:
|
|
.IP \[bu] 2
|
|
\f[C]strace\ \-fvTtt\ \-s\ 256\ \-p\ <mergerfsPID>\ \-o\ /tmp/mergerfs.strace.txt\f[]
|
|
.IP \[bu] 2
|
|
\f[B]Precise\f[] directions on replicating the issue.
|
|
Do not leave \f[B]anything\f[] out.
|
|
.IP \[bu] 2
|
|
Try to recreate the problem in the simplest way using standard programs.
|
|
.SS Contact / Issue submission
|
|
.IP \[bu] 2
|
|
github.com: https://github.com/trapexit/mergerfs/issues
|
|
.IP \[bu] 2
|
|
email: trapexit\@spawn.link
|
|
.IP \[bu] 2
|
|
twitter: https://twitter.com/_trapexit
|
|
.IP \[bu] 2
|
|
reddit: https://www.reddit.com/user/trapexit
|
|
.IP \[bu] 2
|
|
discord: https://discord.gg/MpAr69V
|
|
.SS Support development
|
|
.PP
|
|
This software is free to use and released under a very liberal license
|
|
(ISC).
|
|
That said if you like this software and would like to support its
|
|
development donations are welcome.
|
|
.PP
|
|
At the moment my preference would be GitHub Sponsors only because I am
|
|
part of the matching program.
|
|
That said please use whatever platform you prefer.
|
|
.IP \[bu] 2
|
|
GitHub Sponsors: https://github.com/sponsors/trapexit
|
|
.IP \[bu] 2
|
|
PayPal: https://paypal.me/trapexit
|
|
.IP \[bu] 2
|
|
Patreon: https://www.patreon.com/trapexit
|
|
.IP \[bu] 2
|
|
Ko\-Fi: https://ko\-fi.com/trapexit
|
|
.IP \[bu] 2
|
|
Open Collective: https://opencollective.com/trapexit
|
|
.IP \[bu] 2
|
|
Bitcoin (BTC): bc1qu537hqlnmn2wawx9n7nws0dlkz55h0cd93ny28
|
|
.IP \[bu] 2
|
|
Bitcoin Cash (BCH):
|
|
bitcoincash:qqp0vh9v44us74gaggwjfv9y54zfjmmd7srlqxa3xt
|
|
.IP \[bu] 2
|
|
Bitcoin SV (BSV): 1FkFuxRtt3f8LbkpeUKRZq7gKJFzGSGgZV
|
|
.IP \[bu] 2
|
|
Bitcoin Gold (BTG): AaPuJgJeohPjkB3LxJM6NKGnaHoRJ8ieT3
|
|
.IP \[bu] 2
|
|
Litecoin (LTC): MJQzsHBdNnkyGqCFdcAdHYKugicBmfAXfQ
|
|
.IP \[bu] 2
|
|
Dogecoin (DOGE): DLJNLVe28vZ4SMQSxDJLBQBv57rGtUoWFh
|
|
.IP \[bu] 2
|
|
Ethereum (ETH): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
|
.IP \[bu] 2
|
|
Basic Attention Token (BAT): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
|
.IP \[bu] 2
|
|
Chainlink (LINK): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
|
.IP \[bu] 2
|
|
Reserve Rights (RSR): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
|
.IP \[bu] 2
|
|
Reef Finance (REEF): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
|
.IP \[bu] 2
|
|
Any ERC20 Token: 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
|
.IP \[bu] 2
|
|
Ethereum Classic (ETC): 0x2B6054428e69a1201B6555f7a2aEc0Fba01EAD9F
|
|
.IP \[bu] 2
|
|
Dash (DASH): XvsFrohu8tbjA4E8p7xsc86E2ADxLHGXHL
|
|
.IP \[bu] 2
|
|
Monero (XMR):
|
|
45BBZMrJwPSaFwSoqLVNEggWR2BJJsXxz7bNz8FXnnFo3GyhVJFSCrCFSS7zYwDa9r1TmFmGMxQ2HTntuc11yZ9q1LeCE8f
|
|
.IP \[bu] 2
|
|
Filecoin (FIL): f1wpypkjcluufzo74yha7p67nbxepzizlroockgcy
|
|
.IP \[bu] 2
|
|
LBRY Credits (LBC): bFusyoZPkSuzM2Pr8mcthgvkymaosJZt5r
|
|
.IP \[bu] 2
|
|
Ripple (XRP): r9f6aoxaGD8aymxqH89Ke1PCUPkNiFdZZC
|
|
.IP \[bu] 2
|
|
Tezos (XTZ): tz1ZxerkbbALsuU9XGV9K9fFpuLWnKAGfc1C
|
|
.IP \[bu] 2
|
|
Zcash (ZEC): t1Zo1GGn2T3GrhKvgdtnTsTnWu6tCPaCaHG
|
|
.IP \[bu] 2
|
|
DigiByte (DGB): Sb8r1qTrryY9Sp4YkTE1eeKEGVzgArnE5N
|
|
.IP \[bu] 2
|
|
Namecoin (NMC): NDzb9FkoptGu5QbgetCkodJqo2zE1cTwyb
|
|
.IP \[bu] 2
|
|
Vertcoin (VTC): 3PYdhokAGXJwWrwHRoTywxG4iUDk6EHjKe
|
|
.IP \[bu] 2
|
|
Other crypto currencies: contact me for address
|
|
.SH LINKS
|
|
.IP \[bu] 2
|
|
https://spawn.link
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/mergerfs
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/mergerfs/wiki
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/mergerfs\-tools
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/scorch
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/bbf
|
|
.IP \[bu] 2
|
|
https://github.com/trapexit/backup\-and\-recovery\-howtos
|
|
.SH AUTHORS
|
|
Antonio SJ Musumeci <trapexit@spawn.link>.
|