Browse Source

add tagging branches RW/RO/NW

This allows users to tag a branch as readonly or not for writing regardless
of how the filesystem is mounted. Should simplify deployments and offer
more flexibility.
pull/527/head
Antonio SJ Musumeci 6 years ago
parent
commit
b55ebba4ed
  1. 64
      README.md
  2. 113
      man/mergerfs.1
  3. 8
      src/access.cpp
  4. 216
      src/branch.cpp
  5. 55
      src/branch.hpp
  6. 8
      src/chmod.cpp
  7. 8
      src/chown.cpp
  8. 8
      src/config.cpp
  9. 5
      src/config.hpp
  10. 10
      src/create.cpp
  11. 14
      src/fs.cpp
  12. 4
      src/fs.hpp
  13. 17
      src/fs_glob.cpp
  14. 4
      src/fs_glob.hpp
  15. 18
      src/fs_info.cpp
  16. 5
      src/fs_info.hpp
  17. 8
      src/getattr.cpp
  18. 39
      src/getxattr.cpp
  19. 8
      src/ioctl.cpp
  20. 32
      src/link.cpp
  21. 9
      src/listxattr.cpp
  22. 10
      src/mkdir.cpp
  23. 10
      src/mknod.cpp
  24. 8
      src/open.cpp
  25. 30
      src/option_parser.cpp
  26. 37
      src/policy.hpp
  27. 94
      src/policy_all.cpp
  28. 153
      src/policy_epall.cpp
  29. 165
      src/policy_epff.cpp
  30. 235
      src/policy_eplfs.cpp
  31. 235
      src/policy_eplus.cpp
  32. 235
      src/policy_epmfs.cpp
  33. 4
      src/policy_eprand.cpp
  34. 2
      src/policy_erofs.cpp
  35. 51
      src/policy_error.hpp
  36. 100
      src/policy_ff.cpp
  37. 2
      src/policy_invalid.cpp
  38. 146
      src/policy_lfs.cpp
  39. 145
      src/policy_lus.cpp
  40. 141
      src/policy_mfs.cpp
  41. 200
      src/policy_newest.cpp
  42. 4
      src/policy_rand.cpp
  43. 10
      src/readdir.cpp
  44. 8
      src/readlink.cpp
  45. 8
      src/removexattr.cpp
  46. 40
      src/rename.cpp
  47. 8
      src/rmdir.cpp
  48. 117
      src/setxattr.cpp
  49. 18
      src/statfs.cpp
  50. 12
      src/str.cpp
  51. 4
      src/str.hpp
  52. 10
      src/symlink.cpp
  53. 8
      src/truncate.cpp
  54. 8
      src/unlink.cpp
  55. 8
      src/utimens.cpp
  56. 18
      src/write.cpp
  57. 25
      src/write_buf.cpp

64
README.md

@ -8,7 +8,7 @@ mergerfs - a featureful union filesystem
# SYNOPSIS
mergerfs -o<options> <srcmounts> <mountpoint>
mergerfs -o<options> <branches> <mountpoint>
# DESCRIPTION
@ -81,11 +81,13 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
**NOTE:** Options are evaluated in the order listed so if the options are **func.rmdir=rand,category.action=ff** the **action** category setting will override the **rmdir** setting.
### srcmounts
### branches
The srcmounts (source mounts) argument is a colon (':') delimited list of paths to be included in the pool. It does not matter if the paths are on the same or different drives nor does it matter the filesystem. Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
The 'branches' (formerly 'srcmounts') argument is a colon (':') delimited list of paths to be pooled together. It does not matter if the paths are on the same or different drives nor does it matter the filesystem. Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
To make it easier to include multiple source mounts mergerfs supports [globbing](http://linux.die.net/man/7/glob). **The globbing tokens MUST be escaped when using via the shell else the shell itself will expand it.**
To make it easier to include multiple branches mergerfs supports [globbing](http://linux.die.net/man/7/glob). **The globbing tokens MUST be escaped when using via the shell else the shell itself will apply the glob itself.**
Each branch can have a suffix of `=RW` (read / write), `=RO` (read only), or `=NW` (no writes). These suffixes work with globs as well and will apply to each path found. `RW` is the default behavior and those paths will be eligible for all policy categories. `RO` will exclude those paths from `create` and `action` policies (just as a filesystem being mounted `ro` would). `NW` will exclude those paths from `create` policies (you can't create but you can change / delete).
```
$ mergerfs -o defaults,allow_other,use_ino /mnt/disk\*:/mnt/cdrom /media/drives
@ -176,34 +178,38 @@ Policies, as described below, are of two core types. `path preserving` and `non-
All policies which start with `ep` (**epff**, **eplfs**, **eplus**, **epmfs**, **eprand**) are `path preserving`. `ep` stands for `existing path`.
As the descriptions explain a path preserving policy will only consider drives where the relative path being accessed already exists.
A path preserving policy will only consider drives where the relative path being accessed already exists.
When using non-path preserving policies paths will be cloned to target drives as necessary.
#### Policy descriptions
All **create** policies will filter out branches which are mounted **read only** or tagged as **read only** or **no write**. All **action** policies will filter out branches which are mounted or tagged as **read only**.
If all branches are filtered an error will be returned. Typically EROFS or ENOSPC.
| Policy | Description |
|------------------|------------------------------------------------------------|
| all | Search category: acts like **ff**. Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **ff**. It will exclude readonly drives and those with free space less than **minfreespace**. |
| epall (existing path, all) | Search category: acts like **epff**. Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all existing paths found. **create** works like **epff**. Excludes readonly drives and those with free space less than **minfreespace**. |
| epff (existing path, first found) | Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found where the relative path already exists. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). Falls back to **ff**. |
| eplfs (existing path, least free space) | Of all the drives on which the relative path exists choose the drive with the least free space. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **lfs**. |
| eplus (existing path, least used space) | Of all the drives on which the relative path exists choose the drive with the least used space. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **lus**. |
| epmfs (existing path, most free space) | Of all the drives on which the relative path exists choose the drive with the most free space. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. |
| all | Search category: acts like **ff**. Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. It will exclude branches with free space less than **minfreespace**. |
| epall (existing path, all) | Search category: acts like **epff**. Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all existing paths found. **create** works like **epff**. Excludes branches with free space less than **minfreespace**. |
| epff (existing path, first found) | Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found where the relative path already exists. For **create** category functions it will exclude branches with free space less than **minfreespace**. |
| eplfs (existing path, least free space) | Of all the drives on which the relative path exists choose the drive with the least free space. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| eplus (existing path, least used space) | Of all the drives on which the relative path exists choose the drive with the least used space. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| epmfs (existing path, most free space) | Of all the drives on which the relative path exists choose the drive with the most free space. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| eprand (existing path, random) | Calls **epall** and then randomizes. Otherwise behaves the same as **epall**. |
| erofs | Exclusively return **-1** with **errno** set to **EROFS** (Read-only filesystem). By setting **create** functions to this you can in effect turn the filesystem mostly readonly. |
| ff (first found) | Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). |
| lfs (least free space) | Pick the drive with the least available free space. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. |
| lus (least used space) | Pick the drive with the least used space. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace**. Falls back to **mfs**. |
| mfs (most free space) | Pick the drive with the most available free space. For **create** category functions it will exclude readonly drives. Falls back to **ff**. |
| newest | Pick the file / directory with the largest mtime. For **create** category functions it will exclude readonly drives and those with free space less than **minfreespace** (unless there is no other option). |
| erofs | Exclusively return **-1** with **errno** set to **EROFS** (Read-only filesystem). |
| ff (first found) | Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| lfs (least free space) | Pick the drive with the least available free space. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| lus (least used space) | Pick the drive with the least used space. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| mfs (most free space) | Pick the drive with the most available free space. |
| newest | Pick the file / directory with the largest mtime. For **create** category functions it will exclude those with free space less than **minfreespace**. |
| rand (random) | Calls **all** and then randomizes. |
#### Defaults ####
| Category | Policy |
|----------|--------|
| action | all |
| action | epall |
| create | epmfs |
| search | ff |
@ -253,7 +259,7 @@ The above behavior will help minimize the likelihood of EXDEV being returned but
#### statvfs ####
[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 it's space. Filesystems mounted further down the tree of the src mounts will not be included.
[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 it's space. Filesystems mounted further down the tree of the branch will not be included.
# BUILDING
@ -328,7 +334,9 @@ Any changes made at runtime are **not** persisted. If you wish for values to per
Use `xattr -l /mount/point/.mergerfs` to see all supported keys. Some are informational and therefore readonly.
###### user.mergerfs.srcmounts ######
###### user.mergerfs.branches ######
**NOTE:** formerly `user.mergerfs.srcmounts` but said key is still supported.
Used to query or modify the list of source mounts. When modifying there are several shortcuts to easy manipulation of the list.
@ -341,7 +349,7 @@ Used to query or modify the list of source mounts. When modifying there are seve
| -< | remove first in list |
| -> | remove last in list |
`xattr -w user.mergerfs.srcmounts +</mnt/drive3 /mnt/pool/.mergerfs`
`xattr -w user.mergerfs.branches +</mnt/drive3 /mnt/pool/.mergerfs`
###### minfreespace ######
@ -365,7 +373,7 @@ Output: the policy string except for categories where its funcs have multiple ty
```
[trapexit:/mnt/mergerfs] $ xattr -l .mergerfs
user.mergerfs.srcmounts: /mnt/a:/mnt/b
user.mergerfs.branches: /mnt/a:/mnt/b
user.mergerfs.minfreespace: 4294967295
user.mergerfs.moveonenospc: false
...
@ -377,16 +385,16 @@ ff
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
newest
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.srcmounts +/mnt/c .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.srcmounts .mergerfs
[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.srcmounts =/mnt/c .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.srcmounts .mergerfs
[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.srcmounts '+</mnt/a:/mnt/b' .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.srcmounts .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches '+</mnt/a:/mnt/b' .mergerfs
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
/mnt/a:/mnt/b:/mnt/c
```

113
man/mergerfs.1

@ -8,7 +8,7 @@
mergerfs \- a featureful union filesystem
.SH SYNOPSIS
.PP
mergerfs \-o<options> <srcmounts> <mountpoint>
mergerfs \-o<options> <branches> <mountpoint>
.SH DESCRIPTION
.PP
\f[B]mergerfs\f[] is a union filesystem geared towards simplifying
@ -202,10 +202,10 @@ Example: \f[B]category.create=mfs\f[]
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 srcmounts
.SS branches
.PP
The srcmounts (source mounts) argument is a colon (\[aq]:\[aq])
delimited list of paths to be included in the pool.
The \[aq]branches\[aq] (formerly \[aq]srcmounts\[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.
Used and available space will not be duplicated for paths on the same
@ -213,10 +213,22 @@ 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
To make it easier to include multiple source mounts mergerfs supports
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 expand it.\f[]
the shell itself will apply the glob itself.\f[]
.PP
Each branch can have a suffix of \f[C]=RW\f[] (read / write),
\f[C]=RO\f[] (read only), or \f[C]=NW\f[] (no writes).
These suffixes work with globs as well and will apply to each path
found.
\f[C]RW\f[] is the default behavior and those paths will be eligible for
all policy categories.
\f[C]RO\f[] will exclude those paths from \f[C]create\f[] and
\f[C]action\f[] policies (just as a filesystem being mounted \f[C]ro\f[]
would).
\f[C]NW\f[] will exclude those paths from \f[C]create\f[] policies (you
can\[aq]t create but you can change / delete).
.IP
.nf
\f[C]
@ -413,13 +425,21 @@ All policies which start with \f[C]ep\f[] (\f[B]epff\f[],
\f[C]path\ preserving\f[].
\f[C]ep\f[] stands for \f[C]existing\ path\f[].
.PP
As the descriptions explain a path preserving policy will only consider
drives where the relative path being accessed already exists.
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.
.SS Policy descriptions
.PP
All \f[B]create\f[] policies will filter out branches which are mounted
\f[B]read only\f[] or tagged as \f[B]read only\f[] or \f[B]no write\f[].
All \f[B]action\f[] policies will filter out branches which are mounted
or tagged as \f[B]read only\f[].
.PP
If all branches are filtered an error will be returned.
Typically EROFS or ENOSPC.
.PP
.TS
tab(@);
lw(16.6n) lw(53.4n).
@ -435,9 +455,9 @@ T}@T{
Search category: acts like \f[B]ff\f[].
Action category: apply to all found.
Create category: for \f[B]mkdir\f[], \f[B]mknod\f[], and
\f[B]symlink\f[] it will apply to all found.
\f[B]symlink\f[] it will apply to all branches.
\f[B]create\f[] works like \f[B]ff\f[].
It will exclude readonly drives and those with free space less than
It will exclude branches with free space less than
\f[B]minfreespace\f[].
T}
T{
@ -448,8 +468,7 @@ Action category: apply to all found.
Create category: for \f[B]mkdir\f[], \f[B]mknod\f[], and
\f[B]symlink\f[] it will apply to all existing paths found.
\f[B]create\f[] works like \f[B]epff\f[].
Excludes readonly drives and those with free space less than
\f[B]minfreespace\f[].
Excludes branches with free space less than \f[B]minfreespace\f[].
T}
T{
epff (existing path, first found)
@ -457,37 +476,32 @@ T}@T{
Given the order of the drives, as defined at mount time or configured at
runtime, act on the first one found where the relative path already
exists.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[] (unless there
is no other option).
Falls back to \f[B]ff\f[].
For \f[B]create\f[] category functions it will exclude branches with
free space less than \f[B]minfreespace\f[].
T}
T{
eplfs (existing path, least free space)
T}@T{
Of all the drives on which the relative path exists choose the drive
with the least free space.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[].
Falls back to \f[B]lfs\f[].
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
eplus (existing path, least used space)
T}@T{
Of all the drives on which the relative path exists choose the drive
with the least used space.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[].
Falls back to \f[B]lus\f[].
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
epmfs (existing path, most free space)
T}@T{
Of all the drives on which the relative path exists choose the drive
with the most free space.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[].
Falls back to \f[B]mfs\f[].
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
eprand (existing path, random)
@ -500,48 +514,40 @@ erofs
T}@T{
Exclusively return \f[B]\-1\f[] with \f[B]errno\f[] set to
\f[B]EROFS\f[] (Read\-only filesystem).
By setting \f[B]create\f[] functions to this you can in effect turn the
filesystem mostly readonly.
T}
T{
ff (first found)
T}@T{
Given the order of the drives, as defined at mount time or configured at
runtime, act on the first one found.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[] (unless there
is no other option).
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
lfs (least free space)
T}@T{
Pick the drive with the least available free space.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[].
Falls back to \f[B]mfs\f[].
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
lus (least used space)
T}@T{
Pick the drive with the least used space.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[].
Falls back to \f[B]mfs\f[].
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
mfs (most free space)
T}@T{
Pick the drive with the most available free space.
For \f[B]create\f[] category functions it will exclude readonly drives.
Falls back to \f[B]ff\f[].
T}
T{
newest
T}@T{
Pick the file / directory with the largest mtime.
For \f[B]create\f[] category functions it will exclude readonly drives
and those with free space less than \f[B]minfreespace\f[] (unless there
is no other option).
For \f[B]create\f[] category functions it will exclude those with free
space less than \f[B]minfreespace\f[].
T}
T{
rand (random)
@ -563,7 +569,7 @@ _
T{
action
T}@T{
all
epall
T}
T{
create
@ -694,7 +700,7 @@ 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 it\[aq]s space.
Filesystems mounted further down the tree of the src mounts will not be
Filesystems mounted further down the tree of the branch will not be
included.
.SH BUILDING
.PP
@ -787,7 +793,10 @@ wherever you configure the mounting of mergerfs (/etc/fstab).
Use \f[C]xattr\ \-l\ /mount/point/.mergerfs\f[] to see all supported
keys.
Some are informational and therefore readonly.
.SS user.mergerfs.srcmounts
.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 source mounts.
When modifying there are several shortcuts to easy manipulation of the
@ -834,7 +843,7 @@ remove last in list
T}
.TE
.PP
\f[C]xattr\ \-w\ user.mergerfs.srcmounts\ +</mnt/drive3\ /mnt/pool/.mergerfs\f[]
\f[C]xattr\ \-w\ user.mergerfs.branches\ +</mnt/drive3\ /mnt/pool/.mergerfs\f[]
.SS minfreespace
.PP
Input: interger with an optional multiplier suffix.
@ -858,7 +867,7 @@ In that case it will be a comma separated list
.nf
\f[C]
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-l\ .mergerfs
user.mergerfs.srcmounts:\ /mnt/a:/mnt/b
user.mergerfs.branches:\ /mnt/a:/mnt/b
user.mergerfs.minfreespace:\ 4294967295
user.mergerfs.moveonenospc:\ false
\&...
@ -870,16 +879,16 @@ ff
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.category.search\ .mergerfs
newest
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-w\ user.mergerfs.srcmounts\ +/mnt/c\ .mergerfs
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.srcmounts\ .mergerfs
[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.srcmounts\ =/mnt/c\ .mergerfs
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.srcmounts\ .mergerfs
[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.srcmounts\ \[aq]+</mnt/a:/mnt/b\[aq]\ .mergerfs
[trapexit:/mnt/mergerfs]\ $\ xattr\ \-p\ user.mergerfs.srcmounts\ .mergerfs
[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

8
src/access.cpp

@ -32,7 +32,7 @@ using mergerfs::Category;
static
int
_access(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const int mask)
@ -41,7 +41,7 @@ _access(Policy::Func::Search searchFunc,
string fullpath;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -63,10 +63,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _access(config.access,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
mask);

216
src/branch.cpp

@ -0,0 +1,216 @@
/*
ISC License
Copyright (c) 2018, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "branch.hpp"
#include "fs.hpp"
#include "fs_glob.hpp"
#include "str.hpp"
#include <fnmatch.h>
#include <string>
using std::string;
using std::vector;
bool
Branch::ro(void) const
{
return (mode == Branch::RO);
}
bool
Branch::ro_or_nw(void) const
{
return ((mode == Branch::RO) ||
(mode == Branch::NW));
}
string
Branches::to_string(const bool mode_) const
{
string tmp;
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
tmp += branch.path;
if(mode_)
{
tmp += '=';
switch(branch.mode)
{
default:
case Branch::RW:
tmp += "RW";
break;
case Branch::RO:
tmp += "RO";
break;
case Branch::NW:
tmp += "NW";
break;
}
}
tmp += ':';
}
if(*tmp.rbegin() == ':')
tmp.erase(tmp.size() - 1);
return tmp;
}
void
Branches::to_paths(vector<string> &vec_) const
{
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
vec_.push_back(branch.path);
}
}
static
void
parse(const string &str_,
Branches &branches_)
{
string str;
Branch branch;
vector<string> globbed;
str = str_;
branch.mode = Branch::INVALID;
if(str::ends_with(str,"=RO"))
branch.mode = Branch::RO;
else if(str::ends_with(str,"=RW"))
branch.mode = Branch::RW;
else if(str::ends_with(str,"=NW"))
branch.mode = Branch::NW;
if(branch.mode != Branch::INVALID)
str.resize(str.size() - 3);
else
branch.mode = Branch::RW;
fs::glob(str,globbed);
fs::realpathize(globbed);
for(size_t i = 0; i < globbed.size(); i++)
{
branch.path = globbed[i];
branches_.push_back(branch);
}
}
void
Branches::set(const std::string &str_)
{
vector<string> paths;
clear();
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches branches;
parse(paths[i],branches);
insert(end(),
branches.begin(),
branches.end());
}
}
void
Branches::add_begin(const std::string &str_)
{
vector<string> paths;
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches branches;
parse(paths[i],branches);
insert(begin(),
branches.begin(),
branches.end());
}
}
void
Branches::add_end(const std::string &str_)
{
vector<string> paths;
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches branches;
parse(paths[i],branches);
insert(end(),
branches.begin(),
branches.end());
}
}
void
Branches::erase_begin(void)
{
erase(begin());
}
void
Branches::erase_end(void)
{
pop_back();
}
void
Branches::erase_fnmatch(const std::string &str_)
{
vector<string> patterns;
str::split(patterns,str_,':');
for(iterator i = begin(); i != end();)
{
int match = FNM_NOMATCH;
for(vector<string>::const_iterator pi = patterns.begin();
pi != patterns.end() && match != 0;
++pi)
{
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
}
i = ((match == 0) ? erase(i) : (i+1));
}
}

55
src/branch.hpp

@ -0,0 +1,55 @@
/*
ISC License
Copyright (c) 2018, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include <vector>
struct Branch
{
enum Mode
{
INVALID,
RO,
RW,
NW
};
Mode mode;
std::string path;
bool ro(void) const;
bool ro_or_nw(void) const;
};
class Branches : public std::vector<Branch>
{
public:
std::string to_string(const bool mode_ = false) const;
void to_paths(std::vector<std::string> &vec_) const;
public:
void set(const std::string &str_);
void add_begin(const std::string &str_);
void add_end(const std::string &str_);
void erase_begin(void);
void erase_end(void);
void erase_fnmatch(const std::string &str_);
};

8
src/chmod.cpp

@ -68,7 +68,7 @@ _chmod_loop(const vector<const string*> &basepaths,
static
int
_chmod(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const mode_t mode)
@ -76,7 +76,7 @@ _chmod(Policy::Func::Action actionFunc,
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -94,10 +94,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _chmod(config.chmod,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
mode);

8
src/chown.cpp

@ -71,7 +71,7 @@ _chown_loop(const vector<const string*> &basepaths,
static
int
_chown(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const uid_t uid,
@ -80,7 +80,7 @@ _chown(Policy::Func::Action actionFunc,
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -99,10 +99,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _chown(config.chown,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
uid,

8
src/config.cpp

@ -35,8 +35,8 @@ namespace mergerfs
{
Config::Config()
: destmount(),
srcmounts(),
srcmountslock(),
branches(),
branches_lock(),
minfreespace(MINFREESPACE_DEFAULT),
moveonenospc(false),
direct_io(false),
@ -70,9 +70,9 @@ namespace mergerfs
POLICYINIT(utimens),
controlfile("/.mergerfs")
{
pthread_rwlock_init(&srcmountslock,NULL);
pthread_rwlock_init(&branches_lock,NULL);
set_category_policy("action","all");
set_category_policy("action","epall");
set_category_policy("create","epmfs");
set_category_policy("search","ff");
}

5
src/config.hpp

@ -16,6 +16,7 @@
#pragma once
#include "branch.hpp"
#include "fusefunc.hpp"
#include "policy.hpp"
@ -42,8 +43,8 @@ namespace mergerfs
public:
std::string destmount;
std::vector<std::string> srcmounts;
mutable pthread_rwlock_t srcmountslock;
Branches branches;
mutable pthread_rwlock_t branches_lock;
uint64_t minfreespace;
bool moveonenospc;
bool direct_io;

10
src/create.cpp

@ -73,7 +73,7 @@ static
int
_create(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const mode_t mode,
@ -89,11 +89,11 @@ _create(Policy::Func::Search searchFunc,
fusedirpath = fs::path::dirname(fusepath);
rv = searchFunc(srcmounts,fusedirpath,minfreespace,existingpaths);
rv = searchFunc(branches_,fusedirpath,minfreespace,existingpaths);
if(rv == -1)
return -errno;
rv = createFunc(srcmounts,fusedirpath,minfreespace,createpaths);
rv = createFunc(branches_,fusedirpath,minfreespace,createpaths);
if(rv == -1)
return -errno;
@ -118,11 +118,11 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _create(config.getattr,
config.create,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
mode,

14
src/fs.cpp

@ -81,15 +81,15 @@ namespace fs
}
void
findallfiles(const vector<string> &srcmounts,
findallfiles(const vector<string> &basepaths,
const char *fusepath,
vector<string> &paths)
{
string fullpath;
for(size_t i = 0, ei = srcmounts.size(); i != ei; i++)
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
fs::path::make(&srcmounts[i],fusepath,fullpath);
fs::path::make(&basepaths[i],fusepath,fullpath);
if(!fs::exists(fullpath))
continue;
@ -99,7 +99,7 @@ namespace fs
}
int
findonfs(const vector<string> &srcmounts,
findonfs(const vector<string> &basepaths,
const string &fusepath,
const int fd,
string &basepath)
@ -114,9 +114,9 @@ namespace fs
return -1;
dev = st.st_dev;
for(size_t i = 0, ei = srcmounts.size(); i != ei; i++)
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
fs::path::make(&srcmounts[i],fusepath,fullpath);
fs::path::make(&basepaths[i],fusepath,fullpath);
rv = fs::lstat(fullpath,st);
if(rv == -1)
@ -125,7 +125,7 @@ namespace fs
if(st.st_dev != dev)
continue;
basepath = srcmounts[i];
basepath = basepaths[i];
return 0;
}

4
src/fs.hpp

@ -36,11 +36,11 @@ namespace fs
int spaceused(const string *path_,
uint64_t *spaceavail_);
void findallfiles(const vector<string> &srcmounts,
void findallfiles(const vector<string> &basepaths,
const char *fusepath,
vector<string> &paths);
int findonfs(const vector<string> &srcmounts,
int findonfs(const vector<string> &basepaths,
const string &fusepath,
const int fd,
string &basepath);

17
src/fs_glob.cpp

@ -27,25 +27,16 @@ using std::vector;
namespace fs
{
void
glob(const vector<string> &patterns_,
vector<string> &strs_)
glob(const string &pattern_,
vector<string> &strs_)
{
int flags;
size_t veclen;
glob_t gbuf = {0};
veclen = patterns_.size();
if(veclen == 0)
return;
flags = GLOB_NOCHECK;
::glob(patterns_[0].c_str(),flags,NULL,&gbuf);
flags = GLOB_APPEND|GLOB_NOCHECK;
for(size_t i = 1; i < veclen; i++)
::glob(patterns_[i].c_str(),flags,NULL,&gbuf);
::glob(pattern_.c_str(),flags,NULL,&gbuf);
for(size_t i = 0; i < gbuf.gl_pathc; ++i)
for(size_t i = 0; i < gbuf.gl_pathc; i++)
strs_.push_back(gbuf.gl_pathv[i]);
::globfree(&gbuf);

4
src/fs_glob.hpp

@ -23,6 +23,6 @@ namespace fs
using std::vector;
void
glob(const vector<string> &patterns_,
vector<string> &strs_);
glob(const string &pattern_,
vector<string> &strs_);
}

18
src/fs_info.cpp

@ -47,22 +47,4 @@ namespace fs
return rv;
}
int
info(const string *basepath_,
const char *relpath_,
fs::info_t *info_)
{
int rv;
string fullpath;
struct stat st;
fullpath = fs::path::make(basepath_,relpath_);
rv = fs::lstat(fullpath,st);
if(rv == -1)
return -1;
return fs::info(basepath_,info_);
}
}

5
src/fs_info.hpp

@ -24,11 +24,6 @@
namespace fs
{
int
info(const std::string *basepath_,
const char *relpath_,
fs::info_t *info_);
int
info(const std::string *path_,
fs::info_t *info_);

8
src/getattr.cpp

@ -60,7 +60,7 @@ _getattr_controlfile(struct stat &st)
static
int
_getattr(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
struct stat &st,
@ -71,7 +71,7 @@ _getattr(Policy::Func::Search searchFunc,
string fullpath;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -104,10 +104,10 @@ namespace mergerfs
return _getattr_controlfile(*st);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _getattr(config.getattr,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
*st,

39
src/getxattr.cpp

@ -105,7 +105,15 @@ void
_getxattr_controlfile_srcmounts(const Config &config,
string &attrvalue)
{
attrvalue = str::join(config.srcmounts,':');
attrvalue = config.branches.to_string();
}
static
void
_getxattr_controlfile_branches(const Config &config,
string &attrvalue)
{
attrvalue = config.branches.to_string(true);
}
static
@ -216,6 +224,8 @@ _getxattr_controlfile(const Config &config,
case 3:
if(attr[2] == "srcmounts")
_getxattr_controlfile_srcmounts(config,attrvalue);
else if(attr[2] == "branches")
_getxattr_controlfile_branches(config,attrvalue);
else if(attr[2] == "minfreespace")
_getxattr_controlfile_uint64_t(config.minfreespace,attrvalue);
else if(attr[2] == "moveonenospc")
@ -289,15 +299,18 @@ _getxattr_from_string(char *destbuf,
static
int
_getxattr_user_mergerfs_allpaths(const vector<string> &srcmounts,
const char *fusepath,
char *buf,
const size_t count)
_getxattr_user_mergerfs_allpaths(const Branches &branches_,
const char *fusepath,
char *buf,
const size_t count)
{
string concated;
vector<string> paths;
vector<string> branches;
branches_.to_paths(branches);
fs::findallfiles(srcmounts,fusepath,paths);
fs::findallfiles(branches,fusepath,paths);
concated = str::join(paths,'\0');
@ -309,7 +322,7 @@ int
_getxattr_user_mergerfs(const string &basepath,
const char *fusepath,
const string &fullpath,
const vector<string> &srcmounts,
const Branches &branches_,
const char *attrname,
char *buf,
const size_t count)
@ -325,7 +338,7 @@ _getxattr_user_mergerfs(const string &basepath,
else if(attr[2] == "fullpath")
return _getxattr_from_string(buf,count,fullpath);
else if(attr[2] == "allpaths")
return _getxattr_user_mergerfs_allpaths(srcmounts,fusepath,buf,count);
return _getxattr_user_mergerfs_allpaths(branches_,fusepath,buf,count);
return -ENOATTR;
}
@ -333,7 +346,7 @@ _getxattr_user_mergerfs(const string &basepath,
static
int
_getxattr(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const size_t minfreespace,
const char *fusepath,
const char *attrname,
@ -344,14 +357,14 @@ _getxattr(Policy::Func::Search searchFunc,
string fullpath;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
fs::path::make(basepaths[0],fusepath,fullpath);
if(str::isprefix(attrname,"user.mergerfs."))
rv = _getxattr_user_mergerfs(*basepaths[0],fusepath,fullpath,srcmounts,attrname,buf,count);
rv = _getxattr_user_mergerfs(*basepaths[0],fusepath,fullpath,branches_,attrname,buf,count);
else
rv = _lgetxattr(fullpath,attrname,buf,count);
@ -385,10 +398,10 @@ namespace mergerfs
return -config.xattr;
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _getxattr(config.getxattr,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
attrname,

8
src/ioctl.cpp

@ -69,7 +69,7 @@ _ioctl_file(fuse_file_info *ffi,
static
int
_ioctl_dir_base(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const unsigned long cmd,
@ -80,7 +80,7 @@ _ioctl_dir_base(Policy::Func::Search searchFunc,
string fullpath;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -108,10 +108,10 @@ _ioctl_dir(fuse_file_info *ffi,
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _ioctl_dir_base(config.getattr,
config.srcmounts,
config.branches,
config.minfreespace,
di->fusepath.c_str(),
cmd,

32
src/link.cpp

@ -83,7 +83,7 @@ static
int
_link_create_path(Policy::Func::Search searchFunc,
Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *oldfusepath,
const char *newfusepath)
@ -93,13 +93,13 @@ _link_create_path(Policy::Func::Search searchFunc,
vector<const string*> oldbasepaths;
vector<const string*> newbasepaths;
rv = actionFunc(srcmounts,oldfusepath,minfreespace,oldbasepaths);
rv = actionFunc(branches_,oldfusepath,minfreespace,oldbasepaths);
if(rv == -1)
return -errno;
newfusedirpath = fs::path::dirname(newfusepath);
rv = searchFunc(srcmounts,newfusedirpath,minfreespace,newbasepaths);
rv = searchFunc(branches_,newfusedirpath,minfreespace,newbasepaths);
if(rv == -1)
return -errno;
@ -112,7 +112,7 @@ static
int
_clonepath_if_would_create(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const string &oldbasepath,
const char *oldfusepath,
@ -124,14 +124,14 @@ _clonepath_if_would_create(Policy::Func::Search searchFunc,
newfusedirpath = fs::path::dirname(newfusepath);
rv = createFunc(srcmounts,newfusedirpath,minfreespace,newbasepath);
rv = createFunc(branches_,newfusedirpath,minfreespace,newbasepath);
if(rv == -1)
return -1;
if(oldbasepath != *newbasepath[0])
return (errno=EXDEV,-1);
rv = searchFunc(srcmounts,newfusedirpath,minfreespace,newbasepath);
rv = searchFunc(branches_,newfusedirpath,minfreespace,newbasepath);
if(rv == -1)
return -1;
@ -142,7 +142,7 @@ static
int
_link_preserve_path_core(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const string &oldbasepath,
const char *oldfusepath,
@ -160,7 +160,7 @@ _link_preserve_path_core(Policy::Func::Search searchFunc,
if((rv == -1) && (errno == ENOENT))
{
rv = _clonepath_if_would_create(searchFunc,createFunc,
srcmounts,minfreespace,
branches_,minfreespace,
oldbasepath,
oldfusepath,newfusepath);
if(rv != -1)
@ -174,7 +174,7 @@ static
int
_link_preserve_path_loop(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *oldfusepath,
const char *newfusepath,
@ -186,7 +186,7 @@ _link_preserve_path_loop(Policy::Func::Search searchFunc,
for(size_t i = 0, ei = oldbasepaths.size(); i != ei; i++)
{
error = _link_preserve_path_core(searchFunc,createFunc,
srcmounts,minfreespace,
branches_,minfreespace,
*oldbasepaths[i],
oldfusepath,newfusepath,
error);
@ -200,7 +200,7 @@ int
_link_preserve_path(Policy::Func::Search searchFunc,
Policy::Func::Action actionFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *oldfusepath,
const char *newfusepath)
@ -208,12 +208,12 @@ _link_preserve_path(Policy::Func::Search searchFunc,
int rv;
vector<const string*> oldbasepaths;
rv = actionFunc(srcmounts,oldfusepath,minfreespace,oldbasepaths);
rv = actionFunc(branches_,oldfusepath,minfreespace,oldbasepaths);
if(rv == -1)
return -errno;
return _link_preserve_path_loop(searchFunc,createFunc,
srcmounts,minfreespace,
branches_,minfreespace,
oldfusepath,newfusepath,
oldbasepaths);
}
@ -229,20 +229,20 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
if(config.create->path_preserving() && !config.ignorepponrename)
return _link_preserve_path(config.getattr,
config.link,
config.create,
config.srcmounts,
config.branches,
config.minfreespace,
from,
to);
return _link_create_path(config.link,
config.create,
config.srcmounts,
config.branches,
config.minfreespace,
from,
to);

9
src/listxattr.cpp

@ -44,6 +44,7 @@ _listxattr_controlfile(char *list,
const vector<string> strs =
buildvector<string>
("user.mergerfs.srcmounts")
("user.mergerfs.branches")
("user.mergerfs.minfreespace")
("user.mergerfs.moveonenospc")
("user.mergerfs.dropcacheonclose")
@ -80,7 +81,7 @@ _listxattr_controlfile(char *list,
static
int
_listxattr(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
char *list,
@ -90,7 +91,7 @@ _listxattr(Policy::Func::Search searchFunc,
string fullpath;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -127,10 +128,10 @@ namespace mergerfs
}
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _listxattr(config.listxattr,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
list,

10
src/mkdir.cpp

@ -94,7 +94,7 @@ static
int
_mkdir(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const mode_t mode,
@ -107,11 +107,11 @@ _mkdir(Policy::Func::Search searchFunc,
fusedirpath = fs::path::dirname(fusepath);
rv = searchFunc(srcmounts,fusedirpath,minfreespace,existingpaths);
rv = searchFunc(branches_,fusedirpath,minfreespace,existingpaths);
if(rv == -1)
return -errno;
rv = createFunc(srcmounts,fusedirpath,minfreespace,createpaths);
rv = createFunc(branches_,fusedirpath,minfreespace,createpaths);
if(rv == -1)
return -errno;
@ -130,11 +130,11 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _mkdir(config.getattr,
config.mkdir,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
mode,

10
src/mknod.cpp

@ -98,7 +98,7 @@ static
int
_mknod(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const mode_t mode,
@ -112,11 +112,11 @@ _mknod(Policy::Func::Search searchFunc,
fusedirpath = fs::path::dirname(fusepath);
rv = searchFunc(srcmounts,fusedirpath,minfreespace,existingpaths);
rv = searchFunc(branches_,fusedirpath,minfreespace,existingpaths);
if(rv == -1)
return -errno;
rv = createFunc(srcmounts,fusedirpath,minfreespace,createpaths);
rv = createFunc(branches_,fusedirpath,minfreespace,createpaths);
if(rv == -1)
return -errno;
@ -137,11 +137,11 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _mknod(config.getattr,
config.mknod,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
mode,

8
src/open.cpp

@ -62,7 +62,7 @@ _open_core(const string *basepath_,
static
int
_open(Policy::Func::Search searchFunc_,
const vector<string> &srcmounts_,
const Branches &branches_,
const uint64_t minfreespace_,
const char *fusepath_,
const int flags_,
@ -72,7 +72,7 @@ _open(Policy::Func::Search searchFunc_,
int rv;
vector<const string*> basepaths;
rv = searchFunc_(srcmounts_,fusepath_,minfreespace_,basepaths);
rv = searchFunc_(branches_,fusepath_,minfreespace_,basepaths);
if(rv == -1)
return -errno;
@ -90,10 +90,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _open(config.open,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath_,
ffi_->flags,

30
src/option_parser.cpp

@ -73,14 +73,18 @@ set_kv_option(fuse_args &args,
static
void
set_fsname(fuse_args &args,
const vector<string> &srcmounts)
set_fsname(fuse_args &args,
const Branches &branches_)
{
if(srcmounts.size() > 0)
vector<string> branches;
branches_.to_paths(branches);
if(branches.size() > 0)
{
std::string fsname;
fsname = str::remove_common_prefix_and_join(srcmounts,':');
fsname = str::remove_common_prefix_and_join(branches,':');
set_kv_option(args,"fsname",fsname);
}
@ -257,16 +261,10 @@ process_opt(Config &config,
static
int
process_srcmounts(const char *arg,
Config &config)
process_branches(const char *arg,
Config &config)
{
vector<string> paths;
str::split(paths,arg,':');
fs::glob(paths,config.srcmounts);
fs::realpathize(config.srcmounts);
config.branches.set(arg);
return 0;
}
@ -356,8 +354,8 @@ option_processor(void *data,
break;
case FUSE_OPT_KEY_NONOPT:
rv = config.srcmounts.empty() ?
process_srcmounts(arg,config) :
rv = config.branches.empty() ?
process_branches(arg,config) :
process_destmounts(arg,config);
break;
@ -406,7 +404,7 @@ namespace mergerfs
opts,
::option_processor);
set_fsname(args,config.srcmounts);
set_fsname(args,config.branches);
set_subtype(args);
}
}

37
src/policy.hpp

@ -16,6 +16,7 @@
#pragma once
#include "branch.hpp"
#include "category.hpp"
#include "fs.hpp"
@ -65,7 +66,7 @@ namespace mergerfs
typedef const strvec cstrvec;
typedef const Category::Enum::Type CType;
typedef int (*Ptr)(CType,cstrvec &,const char *,cuint64_t,cstrptrvec &);
typedef int (*Ptr)(CType,const Branches &,const char *,cuint64_t,cstrptrvec &);
template <CType T>
class Base
@ -76,13 +77,13 @@ namespace mergerfs
{}
int
operator()(cstrvec &b,const char *c,cuint64_t d,cstrptrvec &e)
operator()(const Branches &b,const char *c,cuint64_t d,cstrptrvec &e)
{
return func(T,b,c,d,e);
}
int
operator()(cstrvec &b,const string &c,cuint64_t d,cstrptrvec &e)
operator()(const Branches &b,const string &c,cuint64_t d,cstrptrvec &e)
{
return func(T,b,c.c_str(),d,e);
}
@ -95,21 +96,21 @@ namespace mergerfs
typedef Base<Category::Enum::create> Create;
typedef Base<Category::Enum::search> Search;
static int invalid(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int all(CType,cstrvec&,const char*,cuint64_t,cstrptrvec&);
static int epall(CType,cstrvec&,const char*,cuint64_t,cstrptrvec&);
static int epff(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int eplfs(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int eplus(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int epmfs(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int eprand(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int erofs(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int ff(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int lfs(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int lus(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int mfs(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int newest(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int rand(CType,cstrvec&,const char *,cuint64_t,cstrptrvec&);
static int invalid(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int all(CType,const Branches&,const char*,cuint64_t,cstrptrvec&);
static int epall(CType,const Branches&,const char*,cuint64_t,cstrptrvec&);
static int epff(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int eplfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int eplus(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int epmfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int eprand(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int erofs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int ff(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int lfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int lus(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int mfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int newest(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int rand(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
};
private:

94
src/policy_all.cpp

@ -19,6 +19,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <string>
#include <vector>
@ -26,73 +27,56 @@
using std::string;
using std::vector;
static
int
_all_create(const vector<string> &basepaths,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace all
{
int rv;
fs::info_t info;
const string *basepath;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
paths.push_back(basepath);
}
if(paths.empty())
return (errno=ENOENT,-1);
return 0;
}
static
int
_all_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
const string *basepath;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
paths.push_back(basepath);
}
if(paths.empty())
return (errno=ENOENT,-1);
return 0;
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
fs::info_t info;
const Branch *branch;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
}
if(paths.empty())
return (errno=error,-1);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::all(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _all_create(basepaths,minfreespace,paths);
return all::create(branches_,minfreespace,paths);
return _all_other(basepaths,fusepath,paths);
return Policy::Func::epall(type,branches_,fusepath,minfreespace,paths);
}
}

153
src/policy_epall.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <string>
#include <vector>
@ -27,74 +28,124 @@
using std::string;
using std::vector;
static
int
_epall_create(const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace epall
{
int rv;
fs::info_t info;
const string *basepath;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,fusepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
paths.push_back(basepath);
}
if(paths.empty())
return (errno=ENOENT,-1);
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
fs::info_t info;
const Branch *branch;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
}
if(paths.empty())
return (errno=error,-1);
return 0;
}
return 0;
}
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
bool readonly;
const Branch *branch;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
rv = fs::readonly(&branch->path,&readonly);
if(rv == -1)
error_and_continue(error,ENOENT);
if(readonly)
error_and_continue(error,EROFS);
paths.push_back(&branch->path);
}
if(paths.empty())
return (errno=error,-1);
return 0;
}
static
int
_epall_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
const string *basepath;
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
const Branch *branch;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(*basepath,fusepath))
continue;
if(!fs::exists(branch->path,fusepath))
continue;
paths.push_back(basepath);
}
paths.push_back(&branch->path);
}
if(paths.empty())
return (errno=ENOENT,-1);
if(paths.empty())
return (errno=ENOENT,-1);
return 0;
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::epall(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _epall_create(basepaths,fusepath,minfreespace,paths);
return _epall_other(basepaths,fusepath,paths);
switch(type)
{
case Category::Enum::create:
return epall::create(branches_,fusepath,minfreespace,paths);
case Category::Enum::action:
return epall::action(branches_,fusepath,paths);
case Category::Enum::search:
default:
return epall::search(branches_,fusepath,paths);
}
}
}

165
src/policy_epff.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <string>
#include <vector>
@ -28,89 +29,121 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_epff_create(const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace epff
{
int rv;
fs::info_t info;
const string *basepath;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,fusepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
paths.push_back(basepath);
return 0;
}
return (errno=ENOENT,-1);
}
static
int
_epff_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
const string *basepath;
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
fs::info_t info;
const Branch *branch;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
return 0;
}
return (errno=error,-1);
}
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
bool readonly;
const Branch *branch;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
rv = fs::readonly(&branch->path,&readonly);
if(rv == -1)
error_and_continue(error,ENOENT);
if(readonly)
error_and_continue(error,EROFS);
paths.push_back(&branch->path);
return 0;
}
return (errno=error,-1);
}
if(!fs::exists(*basepath,fusepath))
continue;
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
const Branch *branch;
paths.push_back(basepath);
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
return 0;
}
if(!fs::exists(branch->path,fusepath))
continue;
return (errno=ENOENT,-1);
}
paths.push_back(&branch->path);
static
int
_epff(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _epff_create(basepaths,fusepath,minfreespace,paths);
return 0;
}
return _epff_other(basepaths,fusepath,paths);
return (errno=ENOENT,-1);
}
}
namespace mergerfs
{
int
Policy::Func::epff(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _epff(type,basepaths,fusepath,minfreespace,paths);
if(rv == -1)
rv = Policy::Func::ff(type,basepaths,fusepath,minfreespace,paths);
return rv;
switch(type)
{
case Category::Enum::create:
return epff::create(branches_,fusepath,minfreespace,paths);
case Category::Enum::action:
return epff::action(branches_,fusepath,paths);
case Category::Enum::search:
default:
return epff::search(branches_,fusepath,paths);
}
}
}

235
src/policy_eplfs.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <limits>
#include <string>
@ -29,114 +30,156 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_eplfs_create(const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace eplfs
{
int rv;
uint64_t eplfs;
fs::info_t info;
const string *basepath;
const string *eplfsbasepath;
eplfs = std::numeric_limits<uint64_t>::max();
eplfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,fusepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
if(info.spaceavail > eplfs)
continue;
eplfs = info.spaceavail;
eplfsbasepath = basepath;
}
if(eplfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplfsbasepath);
return 0;
}
static
int
_eplfs_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t eplfs;
uint64_t spaceavail;
const string *basepath;
const string *eplfsbasepath;
eplfs = std::numeric_limits<uint64_t>::max();
eplfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
rv = fs::spaceavail(basepath,&spaceavail);
if(rv == -1)
continue;
if(spaceavail > eplfs)
continue;
eplfs = spaceavail;
eplfsbasepath = basepath;
}
if(eplfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplfsbasepath);
return 0;
}
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t eplfs;
fs::info_t info;
const Branch *branch;
const string *eplfsbasepath;
error = ENOENT;
eplfs = std::numeric_limits<uint64_t>::max();
eplfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
if(info.spaceavail > eplfs)
continue;
eplfs = info.spaceavail;
eplfsbasepath = &branch->path;
}
if(eplfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplfsbasepath);
return 0;
}
static
int
_eplfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _eplfs_create(basepaths,fusepath,minfreespace,paths);
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t eplfs;
fs::info_t info;
const Branch *branch;
const string *eplfsbasepath;
error = ENOENT;
eplfs = std::numeric_limits<uint64_t>::max();
eplfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail > eplfs)
continue;
eplfs = info.spaceavail;
eplfsbasepath = &branch->path;
}
if(eplfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplfsbasepath);
return 0;
}
return _eplfs_other(basepaths,fusepath,paths);
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t eplfs;
uint64_t spaceavail;
const Branch *branch;
const string *eplfsbasepath;
eplfs = std::numeric_limits<uint64_t>::max();
eplfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
continue;
rv = fs::spaceavail(&branch->path,&spaceavail);
if(rv == -1)
continue;
if(spaceavail > eplfs)
continue;
eplfs = spaceavail;
eplfsbasepath = &branch->path;
}
if(eplfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplfsbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::eplfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _eplfs(type,basepaths,fusepath,minfreespace,paths);
if(rv == -1)
rv = Policy::Func::lfs(type,basepaths,fusepath,minfreespace,paths);
return rv;
switch(type)
{
case Category::Enum::create:
return eplfs::create(branches_,fusepath,minfreespace,paths);
case Category::Enum::action:
return eplfs::action(branches_,fusepath,paths);
case Category::Enum::search:
default:
return eplfs::search(branches_,fusepath,paths);
}
}
}

235
src/policy_eplus.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <limits>
#include <string>
@ -29,114 +30,156 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_eplus_create(const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace eplus
{
int rv;
uint64_t eplus;
fs::info_t info;
const string *basepath;
const string *eplusbasepath;
eplus = std::numeric_limits<uint64_t>::max();
eplusbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,fusepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
if(info.spaceused >= eplus)
continue;
eplus = info.spaceused;
eplusbasepath = basepath;
}
if(eplusbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplusbasepath);
return 0;
}
static
int
_eplus_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t eplus;
uint64_t spaceused;
const string *basepath;
const string *eplusbasepath;
eplus = 0;
eplusbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
rv = fs::spaceused(basepath,&spaceused);
if(rv == -1)
continue;
if(spaceused >= eplus)
continue;
eplus = spaceused;
eplusbasepath = basepath;
}
if(eplusbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplusbasepath);
return 0;
}
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t eplus;
fs::info_t info;
const Branch *branch;
const string *eplusbasepath;
error = ENOENT;
eplus = std::numeric_limits<uint64_t>::max();
eplusbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
if(info.spaceused >= eplus)
continue;
eplus = info.spaceused;
eplusbasepath = &branch->path;
}
if(eplusbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplusbasepath);
return 0;
}
static
int
_eplus(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _eplus_create(basepaths,fusepath,minfreespace,paths);
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t eplus;
fs::info_t info;
const Branch *branch;
const string *eplusbasepath;
error = ENOENT;
eplus = std::numeric_limits<uint64_t>::max();
eplusbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceused >= eplus)
continue;
eplus = info.spaceused;
eplusbasepath = &branch->path;
}
if(eplusbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplusbasepath);
return 0;
}
return _eplus_other(basepaths,fusepath,paths);
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t eplus;
uint64_t spaceused;
const Branch *branch;
const string *eplusbasepath;
eplus = 0;
eplusbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
continue;
rv = fs::spaceused(&branch->path,&spaceused);
if(rv == -1)
continue;
if(spaceused >= eplus)
continue;
eplus = spaceused;
eplusbasepath = &branch->path;
}
if(eplusbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplusbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::eplus(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _eplus(type,basepaths,fusepath,minfreespace,paths);
if(rv == -1)
rv = Policy::Func::lus(type,basepaths,fusepath,minfreespace,paths);
return rv;
switch(type)
{
case Category::Enum::create:
return eplus::create(branches_,fusepath,minfreespace,paths);
case Category::Enum::action:
return eplus::action(branches_,fusepath,paths);
case Category::Enum::search:
default:
return eplus::search(branches_,fusepath,paths);
}
}
}

235
src/policy_epmfs.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <limits>
#include <string>
@ -29,114 +30,156 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_epmfs_create(const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace epmfs
{
int rv;
uint64_t epmfs;
fs::info_t info;
const string *basepath;
const string *epmfsbasepath;
epmfs = std::numeric_limits<uint64_t>::min();
epmfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,fusepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
if(info.spaceavail < epmfs)
continue;
epmfs = info.spaceavail;
epmfsbasepath = basepath;
}
if(epmfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(epmfsbasepath);
return 0;
}
static
int
_epmfs_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t epmfs;
uint64_t spaceavail;
const string *basepath;
const string *epmfsbasepath;
epmfs = 0;
epmfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
rv = fs::spaceavail(basepath,&spaceavail);
if(rv == -1)
continue;
if(spaceavail < epmfs)
continue;
epmfs = spaceavail;
epmfsbasepath = basepath;
}
if(epmfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(epmfsbasepath);
return 0;
}
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t epmfs;
fs::info_t info;
const Branch *branch;
const string *epmfsbasepath;
error = ENOENT;
epmfs = std::numeric_limits<uint64_t>::min();
epmfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
if(info.spaceavail < epmfs)
continue;
epmfs = info.spaceavail;
epmfsbasepath = &branch->path;
}
if(epmfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(epmfsbasepath);
return 0;
}
static
int
_epmfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _epmfs_create(basepaths,fusepath,minfreespace,paths);
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t epmfs;
fs::info_t info;
const Branch *branch;
const string *epmfsbasepath;
error = ENOENT;
epmfs = std::numeric_limits<uint64_t>::min();
epmfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < epmfs)
continue;
epmfs = info.spaceavail;
epmfsbasepath = &branch->path;
}
if(epmfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(epmfsbasepath);
return 0;
}
return _epmfs_other(basepaths,fusepath,paths);
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t epmfs;
uint64_t spaceavail;
const Branch *branch;
const string *epmfsbasepath;
epmfs = 0;
epmfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
continue;
rv = fs::spaceavail(&branch->path,&spaceavail);
if(rv == -1)
continue;
if(spaceavail < epmfs)
continue;
epmfs = spaceavail;
epmfsbasepath = &branch->path;
}
if(epmfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(epmfsbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::epmfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _epmfs(type,basepaths,fusepath,minfreespace,paths);
if(rv == -1)
rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths);
return rv;
switch(type)
{
case Category::Enum::create:
return epmfs::create(branches_,fusepath,minfreespace,paths);
case Category::Enum::action:
return epmfs::action(branches_,fusepath,paths);
case Category::Enum::search:
default:
return epmfs::search(branches_,fusepath,paths);
}
}
}

4
src/policy_eprand.cpp

@ -28,14 +28,14 @@ namespace mergerfs
{
int
Policy::Func::eprand(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = Policy::Func::epall(type,basepaths,fusepath,minfreespace,paths);
rv = Policy::Func::epall(type,branches_,fusepath,minfreespace,paths);
if(rv == 0)
std::random_shuffle(paths.begin(),paths.end());

2
src/policy_erofs.cpp

@ -27,7 +27,7 @@ namespace mergerfs
{
int
Policy::Func::erofs(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)

51
src/policy_error.hpp

@ -0,0 +1,51 @@
/*
ISC License
Copyright (c) 2018, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#define error_and_continue(CUR,ERR) \
{ \
policy::calc_error(CUR,ERR); \
continue; \
}
namespace policy
{
static
inline
void
calc_error(int &cur_,
int err_)
{
switch(cur_)
{
default:
case ENOENT:
cur_ = err_;
break;
case ENOSPC:
if(err_ != ENOENT)
cur_ = err_;
break;
case EROFS:
if((err_ != ENOENT) && (err_ != ENOSPC))
cur_ = err_;
break;
}
}
}

100
src/policy_ff.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <string>
#include <vector>
@ -27,80 +28,55 @@
using std::string;
using std::vector;
static
int
_ff_create(const vector<string> &basepaths,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace ff
{
int rv;
fs::info_t info;
const string *basepath;
const string *fallback;
fallback = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(fallback == NULL)
fallback = basepath;
if(info.spaceavail < minfreespace)
continue;
paths.push_back(basepath);
return 0;
}
if(fallback == NULL)
return (errno=ENOENT,-1);
paths.push_back(fallback);
return 0;
}
static
int
_ff_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
const string *basepath;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
paths.push_back(basepath);
return 0;
}
return (errno=ENOENT,-1);
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
fs::info_t info;
const Branch *branch;
error = ENOENT;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
return 0;
}
return (errno=error,-1);
}
}
namespace mergerfs
{
int
Policy::Func::ff(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _ff_create(basepaths,minfreespace,paths);
return ff::create(branches_,minfreespace,paths);
return _ff_other(basepaths,fusepath,paths);
return Policy::Func::epff(type,branches_,fusepath,minfreespace,paths);
}
}

2
src/policy_invalid.cpp

@ -27,7 +27,7 @@ namespace mergerfs
{
int
Policy::Func::invalid(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)

146
src/policy_lfs.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <limits>
#include <string>
@ -29,114 +30,65 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_lfs_create(const vector<string> &basepaths,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace lfs
{
int rv;
uint64_t lfs;
fs::info_t info;
const string *basepath;
const string *lfsbasepath;
lfs = std::numeric_limits<uint64_t>::max();
lfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
if(info.spaceavail > lfs)
continue;
lfs = info.spaceavail;
lfsbasepath = basepath;
}
if(lfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(lfsbasepath);
return 0;
}
static
int
_lfs_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t lfs;
uint64_t spaceavail;
const string *basepath;
const string *lfsbasepath;
lfs = std::numeric_limits<uint64_t>::max();
lfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
rv = fs::spaceavail(basepath,&spaceavail);
if(rv == -1)
continue;
if(spaceavail > lfs)
continue;
lfs = spaceavail;
lfsbasepath = basepath;
}
if(lfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(lfsbasepath);
return 0;
}
static
int
_lfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _lfs_create(basepaths,minfreespace,paths);
return _lfs_other(basepaths,fusepath,paths);
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t lfs;
fs::info_t info;
const Branch *branch;
const string *lfsbasepath;
error = ENOENT;
lfs = std::numeric_limits<uint64_t>::max();
lfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
if(info.spaceavail > lfs)
continue;
lfs = info.spaceavail;
lfsbasepath = &branch->path;
}
if(lfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(lfsbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::lfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _lfs(type,basepaths,fusepath,minfreespace,paths);
if(rv == -1)
rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths);
if(type == Category::Enum::create)
return lfs::create(branches_,minfreespace,paths);
return rv;
return Policy::Func::eplfs(type,branches_,fusepath,minfreespace,paths);
}
}

145
src/policy_lus.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <limits>
#include <string>
@ -29,113 +30,65 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_lus_create(const vector<string> &basepaths,
const uint64_t minfreespace,
vector<const string*> &paths)
namespace lus
{
int rv;
uint64_t lus;
fs::info_t info;
const string *basepath;
const string *lusbasepath;
lus = std::numeric_limits<uint64_t>::max();
lusbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < minfreespace)
continue;
if(info.spaceused >= lus)
continue;
lus = info.spaceused;
lusbasepath = basepath;
}
if(lusbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(lusbasepath);
return 0;
}
static
int
_lus_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t lus;
uint64_t spaceused;
const string *basepath;
const string *lusbasepath;
lus = 0;
lusbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
rv = fs::spaceused(basepath,&spaceused);
if(rv == -1)
continue;
if(spaceused >= lus)
continue;
lus = spaceused;
lusbasepath = basepath;
}
if(lusbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(lusbasepath);
return 0;
}
static
int
_lus(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _lus_create(basepaths,minfreespace,paths);
return _lus_other(basepaths,fusepath,paths);
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t lus;
fs::info_t info;
const Branch *branch;
const string *lusbasepath;
error = ENOENT;
lus = std::numeric_limits<uint64_t>::max();
lusbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
if(info.spaceused >= lus)
continue;
lus = info.spaceused;
lusbasepath = &branch->path;
}
if(lusbasepath == NULL)
return (errno=error,-1);
paths.push_back(lusbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::lus(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _lus(type,basepaths,fusepath,minfreespace,paths);
if(rv == -1)
rv = Policy::Func::mfs(type,basepaths,fusepath,minfreespace,paths);
if(type == Category::Enum::create)
return lus::create(branches_,minfreespace,paths);
return rv;
return Policy::Func::eplus(type,branches_,fusepath,minfreespace,paths);
}
}

141
src/policy_mfs.cpp

@ -20,6 +20,7 @@
#include "fs_info.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <string>
#include <vector>
@ -28,109 +29,65 @@ using std::string;
using std::vector;
using mergerfs::Category;
static
int
_mfs_create(const vector<string> &basepaths,
vector<const string*> &paths)
namespace mfs
{
int rv;
uint64_t mfs;
fs::info_t info;
const string *basepath;
const string *mfsbasepath;
mfs = 0;
mfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
rv = fs::info(basepath,&info);
if(rv == -1)
continue;
if(info.readonly)
continue;
if(info.spaceavail < mfs)
continue;
mfs = info.spaceavail;
mfsbasepath = basepath;
}
if(mfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(mfsbasepath);
return 0;
}
static
int
_mfs_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
uint64_t mfs;
uint64_t spaceavail;
const string *basepath;
const string *mfsbasepath;
mfs = 0;
mfsbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath))
continue;
rv = fs::spaceavail(basepath,&spaceavail);
if(rv == -1)
continue;
if(spaceavail < mfs)
continue;
mfs = spaceavail;
mfsbasepath = basepath;
}
if(mfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(mfsbasepath);
return 0;
}
static
int
_mfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _mfs_create(basepaths,paths);
return _mfs_other(basepaths,fusepath,paths);
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
int error;
uint64_t mfs;
fs::info_t info;
const Branch *branch;
const string *mfsbasepath;
error = ENOENT;
mfs = 0;
mfsbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
rv = fs::info(&branch->path,&info);
if(rv == -1)
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
if(info.spaceavail < mfs)
continue;
mfs = info.spaceavail;
mfsbasepath = &branch->path;
}
if(mfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(mfsbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::mfs(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = _mfs(type,basepaths,fusepath,paths);
if(rv == -1)
rv = Policy::Func::ff(type,basepaths,fusepath,minfreespace,paths);
if(type == Category::Enum::create)
return mfs::create(branches_,minfreespace,paths);
return rv;
return Policy::Func::epmfs(type,branches_,fusepath,minfreespace,paths);
}
}

200
src/policy_newest.cpp

@ -14,12 +14,12 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "errno.hpp"
#include "fs.hpp"
#include "fs_exists.hpp"
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include <string>
#include <vector>
@ -30,93 +30,151 @@
using std::string;
using std::vector;
static
int
_newest_create(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
namespace newest
{
int rv;
bool readonly;
time_t newest;
struct stat st;
const string *basepath;
const string *newestbasepath;
newest = std::numeric_limits<time_t>::min();
newestbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
if(!fs::exists(*basepath,fusepath,st))
continue;
if(st.st_mtime < newest)
continue;
rv = fs::readonly(basepath,&readonly);
if(rv == -1)
continue;
if(readonly)
continue;
newest = st.st_mtime;
newestbasepath = basepath;
}
if(newestbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(newestbasepath);
return 0;
}
static
int
create(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
bool readonly;
time_t newest;
struct stat st;
const Branch *branch;
const string *newestbasepath;
error = ENOENT;
newest = std::numeric_limits<time_t>::min();
newestbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath,st))
error_and_continue(error,ENOENT);
if(branch->ro_or_nw())
error_and_continue(error,EROFS);
if(st.st_mtime < newest)
continue;
rv = fs::readonly(&branch->path,&readonly);
if(rv == -1)
error_and_continue(error,ENOENT);
if(readonly)
error_and_continue(error,EROFS);
newest = st.st_mtime;
newestbasepath = &branch->path;
}
if(newestbasepath == NULL)
return (errno=error,-1);
paths.push_back(newestbasepath);
return 0;
}
static
int
_newest_other(const vector<string> &basepaths,
const char *fusepath,
vector<const string*> &paths)
{
time_t newest;
struct stat st;
const string *basepath;
const string *newestbasepath;
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
int rv;
int error;
bool readonly;
time_t newest;
struct stat st;
const Branch *branch;
const string *newestbasepath;
error = ENOENT;
newest = std::numeric_limits<time_t>::min();
newestbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath,st))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
if(st.st_mtime < newest)
continue;
rv = fs::readonly(&branch->path,&readonly);
if(rv == -1)
error_and_continue(error,ENOENT);
if(readonly)
error_and_continue(error,EROFS);
newest = st.st_mtime;
newestbasepath = &branch->path;
}
if(newestbasepath == NULL)
return (errno=error,-1);
paths.push_back(newestbasepath);
return 0;
}
newest = std::numeric_limits<time_t>::min();
newestbasepath = NULL;
for(size_t i = 0, ei = basepaths.size(); i != ei; i++)
{
basepath = &basepaths[i];
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
{
time_t newest;
struct stat st;
const Branch *branch;
const string *newestbasepath;
if(!fs::exists(*basepath,fusepath,st))
continue;
if(st.st_mtime < newest)
continue;
newest = std::numeric_limits<time_t>::min();
newestbasepath = NULL;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
newest = st.st_mtime;
newestbasepath = basepath;
}
if(!fs::exists(branch->path,fusepath,st))
continue;
if(st.st_mtime < newest)
continue;
if(newestbasepath == NULL)
return (errno=ENOENT,-1);
newest = st.st_mtime;
newestbasepath = &branch->path;
}
paths.push_back(newestbasepath);
if(newestbasepath == NULL)
return (errno=ENOENT,-1);
return 0;
paths.push_back(newestbasepath);
return 0;
}
}
namespace mergerfs
{
int
Policy::Func::newest(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
if(type == Category::Enum::create)
return _newest_create(basepaths,fusepath,paths);
return _newest_other(basepaths,fusepath,paths);
switch(type)
{
case Category::Enum::create:
return newest::create(branches_,fusepath,paths);
case Category::Enum::action:
return newest::action(branches_,fusepath,paths);
case Category::Enum::search:
default:
return newest::search(branches_,fusepath,paths);
}
}
}

4
src/policy_rand.cpp

@ -28,14 +28,14 @@ namespace mergerfs
{
int
Policy::Func::rand(const Category::Enum::Type type,
const vector<string> &basepaths,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
{
int rv;
rv = Policy::Func::all(type,basepaths,fusepath,minfreespace,paths);
rv = Policy::Func::all(type,branches_,fusepath,minfreespace,paths);
if(rv == 0)
std::random_shuffle(paths.begin(),paths.end());

10
src/readdir.cpp

@ -45,7 +45,7 @@ using std::vector;
static
int
_readdir(const vector<string> &srcmounts,
_readdir(const Branches &branches_,
const char *dirname,
void *buf,
const fuse_fill_dir_t filler)
@ -54,13 +54,13 @@ _readdir(const vector<string> &srcmounts,
string basepath;
struct stat st = {0};
for(size_t i = 0, ei = srcmounts.size(); i != ei; i++)
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
int rv;
int dirfd;
DIR *dh;
fs::path::make(&srcmounts[i],dirname,basepath);
basepath = fs::path::make(&branches_[i].path,dirname);
dh = fs::opendir(basepath);
if(!dh)
@ -109,9 +109,9 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return ::_readdir(config.srcmounts,
return ::_readdir(config.branches,
di->fusepath.c_str(),
buf,
filler);

8
src/readlink.cpp

@ -93,7 +93,7 @@ _readlink_core(const string *basepath,
static
int
_readlink(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
char *buf,
@ -104,7 +104,7 @@ _readlink(Policy::Func::Search searchFunc,
int rv;
vector<const string*> basepaths;
rv = searchFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -124,10 +124,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _readlink(config.readlink,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
buf,

8
src/removexattr.cpp

@ -69,7 +69,7 @@ _removexattr_loop(const vector<const string*> &basepaths,
static
int
_removexattr(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const char *attrname)
@ -77,7 +77,7 @@ _removexattr(Policy::Func::Action actionFunc,
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -101,10 +101,10 @@ namespace mergerfs
return -config.xattr;
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _removexattr(config.removexattr,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
attrname);

40
src/rename.cpp

@ -100,7 +100,7 @@ static
int
_rename_create_path(Policy::Func::Search searchFunc,
Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *oldfusepath,
const char *newfusepath)
@ -112,20 +112,20 @@ _rename_create_path(Policy::Func::Search searchFunc,
vector<const string*> newbasepath;
vector<const string*> oldbasepaths;
rv = actionFunc(srcmounts,oldfusepath,minfreespace,oldbasepaths);
rv = actionFunc(branches_,oldfusepath,minfreespace,oldbasepaths);
if(rv == -1)
return -errno;
newfusedirpath = fs::path::dirname(newfusepath);
rv = searchFunc(srcmounts,newfusedirpath,minfreespace,newbasepath);
rv = searchFunc(branches_,newfusedirpath,minfreespace,newbasepath);
if(rv == -1)
return -errno;
error = -1;
for(size_t i = 0, ei = srcmounts.size(); i != ei; i++)
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
const string &oldbasepath = srcmounts[i];
const string &oldbasepath = branches_[i].path;
_rename_create_path_core(oldbasepaths,
oldbasepath,*newbasepath[0],
@ -144,7 +144,7 @@ _rename_create_path(Policy::Func::Search searchFunc,
static
int
_clonepath(Policy::Func::Search searchFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const string &dstbasepath,
const string &fusedirpath)
@ -152,7 +152,7 @@ _clonepath(Policy::Func::Search searchFunc,
int rv;
vector<const string*> srcbasepath;
rv = searchFunc(srcmounts,fusedirpath,minfreespace,srcbasepath);
rv = searchFunc(branches_,fusedirpath,minfreespace,srcbasepath);
if(rv == -1)
return -errno;
@ -165,7 +165,7 @@ static
int
_clonepath_if_would_create(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const string &oldbasepath,
const char *oldfusepath,
@ -177,12 +177,12 @@ _clonepath_if_would_create(Policy::Func::Search searchFunc,
newfusedirpath = fs::path::dirname(newfusepath);
rv = createFunc(srcmounts,newfusedirpath,minfreespace,newbasepath);
rv = createFunc(branches_,newfusedirpath,minfreespace,newbasepath);
if(rv == -1)
return rv;
if(oldbasepath == *newbasepath[0])
return _clonepath(searchFunc,srcmounts,minfreespace,oldbasepath,newfusedirpath);
return _clonepath(searchFunc,branches_,minfreespace,oldbasepath,newfusedirpath);
return (errno=EXDEV,-1);
}
@ -191,7 +191,7 @@ static
void
_rename_preserve_path_core(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const vector<const string*> &oldbasepaths,
const string &oldbasepath,
@ -217,7 +217,7 @@ _rename_preserve_path_core(Policy::Func::Search searchFunc,
if((rv == -1) && (errno == ENOENT))
{
rv = _clonepath_if_would_create(searchFunc,createFunc,
srcmounts,minfreespace,
branches_,minfreespace,
oldbasepath,oldfusepath,newfusepath);
if(rv == 0)
rv = fs::rename(oldfullpath,newfullpath);
@ -238,7 +238,7 @@ int
_rename_preserve_path(Policy::Func::Search searchFunc,
Policy::Func::Action actionFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *oldfusepath,
const char *newfusepath)
@ -248,17 +248,17 @@ _rename_preserve_path(Policy::Func::Search searchFunc,
vector<string> toremove;
vector<const string*> oldbasepaths;
rv = actionFunc(srcmounts,oldfusepath,minfreespace,oldbasepaths);
rv = actionFunc(branches_,oldfusepath,minfreespace,oldbasepaths);
if(rv == -1)
return -errno;
error = -1;
for(size_t i = 0, ei = srcmounts.size(); i != ei; i++)
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
const string &oldbasepath = srcmounts[i];
const string &oldbasepath = branches_[i].path;
_rename_preserve_path_core(searchFunc,createFunc,
srcmounts,minfreespace,
branches_,minfreespace,
oldbasepaths,oldbasepath,
oldfusepath,newfusepath,
error,toremove);
@ -281,20 +281,20 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
if(config.create->path_preserving() && !config.ignorepponrename)
return _rename_preserve_path(config.getattr,
config.rename,
config.create,
config.srcmounts,
config.branches,
config.minfreespace,
oldpath,
newpath);
return _rename_create_path(config.getattr,
config.rename,
config.srcmounts,
config.branches,
config.minfreespace,
oldpath,
newpath);

8
src/rmdir.cpp

@ -68,14 +68,14 @@ _rmdir_loop(const vector<const string*> &basepaths,
static
int
_rmdir(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath)
{
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -92,10 +92,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readguard(&config.srcmountslock);
const rwlock::ReadGuard readguard(&config.branches_lock);
return _rmdir(config.rmdir,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath);
}

117
src/setxattr.cpp

@ -48,78 +48,6 @@ _is_attrname_security_capability(const char *attrname_)
return (strcmp(attrname_,SECURITY_CAPABILITY) == 0);
}
static
int
_add_srcmounts(vector<string> &srcmounts,
pthread_rwlock_t &srcmountslock,
const string &destmount,
const string &values,
vector<string>::iterator pos)
{
vector<string> patterns;
vector<string> additions;
str::split(patterns,values,':');
fs::glob(patterns,additions);
fs::realpathize(additions);
if(!additions.empty())
{
const rwlock::WriteGuard wrg(&srcmountslock);
srcmounts.insert(pos,
additions.begin(),
additions.end());
}
return 0;
}
static
int
_erase_srcmounts(vector<string> &srcmounts,
pthread_rwlock_t &srcmountslock,
const string &values)
{
if(srcmounts.empty())
return 0;
vector<string> patterns;
str::split(patterns,values,':');
{
const rwlock::WriteGuard wrg(&srcmountslock);
str::erase_fnmatches(patterns,srcmounts);
}
return 0;
}
static
int
_replace_srcmounts(vector<string> &srcmounts,
pthread_rwlock_t &srcmountslock,
const string &destmount,
const string &values)
{
vector<string> patterns;
vector<string> newmounts;
str::split(patterns,values,':');
fs::glob(patterns,newmounts);
fs::realpathize(newmounts);
{
const rwlock::WriteGuard wrg(&srcmountslock);
srcmounts.swap(newmounts);
}
return 0;
}
static
void
_split_attrval(const string &attrval,
@ -138,10 +66,11 @@ static
int
_setxattr_srcmounts(const string &attrval,
const int flags,
vector<string> &srcmounts,
pthread_rwlock_t &srcmountslock,
const string &destmount)
Branches &branches_,
pthread_rwlock_t &branches_lock)
{
const rwlock::WriteGuard wrg(&branches_lock);
string instruction;
string values;
@ -151,23 +80,25 @@ _setxattr_srcmounts(const string &attrval,
_split_attrval(attrval,instruction,values);
if(instruction == "+")
return _add_srcmounts(srcmounts,srcmountslock,destmount,values,srcmounts.end());
branches_.add_end(values);
else if(instruction == "+<")
return _add_srcmounts(srcmounts,srcmountslock,destmount,values,srcmounts.begin());
branches_.add_begin(values);
else if(instruction == "+>")
return _add_srcmounts(srcmounts,srcmountslock,destmount,values,srcmounts.end());
branches_.add_end(values);
else if(instruction == "-")
return _erase_srcmounts(srcmounts,srcmountslock,values);
branches_.erase_fnmatch(values);
else if(instruction == "-<")
return _erase_srcmounts(srcmounts,srcmountslock,srcmounts.front());
branches_.erase_begin();
else if(instruction == "->")
return _erase_srcmounts(srcmounts,srcmountslock,srcmounts.back());
branches_.erase_end();
else if(instruction == "=")
return _replace_srcmounts(srcmounts,srcmountslock,destmount,values);
branches_.set(values);
else if(instruction.empty())
return _replace_srcmounts(srcmounts,srcmountslock,destmount,values);
branches_.set(values);
else
return -EINVAL;
return -EINVAL;
return 0;
}
static
@ -280,9 +211,13 @@ _setxattr_controlfile(Config &config,
if(attr[2] == "srcmounts")
return _setxattr_srcmounts(attrval,
flags,
config.srcmounts,
config.srcmountslock,
config.destmount);
config.branches,
config.branches_lock);
else if(attr[2] == "branches")
return _setxattr_srcmounts(attrval,
flags,
config.branches,
config.branches_lock);
else if(attr[2] == "minfreespace")
return _setxattr_uint64_t(attrval,
flags,
@ -382,7 +317,7 @@ _setxattr_loop(const vector<const string*> &basepaths,
static
int
_setxattr(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const char *attrname,
@ -393,7 +328,7 @@ _setxattr(Policy::Func::Action actionFunc,
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -428,10 +363,10 @@ namespace mergerfs
return -config.xattr;
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _setxattr(config.setxattr,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
attrname,

18
src/statfs.cpp

@ -64,7 +64,7 @@ _merge_statvfs(struct statvfs * const out,
static
void
_statfs_core(const char *srcmount,
_statfs_core(const string &path_,
unsigned long &min_bsize,
unsigned long &min_frsize,
unsigned long &min_namemax,
@ -74,11 +74,11 @@ _statfs_core(const char *srcmount,
struct stat st;
struct statvfs fsstat;
rv = fs::statvfs(srcmount,fsstat);
rv = fs::statvfs(path_,fsstat);
if(rv == -1)
return;
rv = fs::stat(srcmount,st);
rv = fs::stat(path_,st);
if(rv == -1)
return;
@ -94,17 +94,17 @@ _statfs_core(const char *srcmount,
static
int
_statfs(const vector<string> &srcmounts,
struct statvfs &fsstat)
_statfs(const Branches &branches_,
struct statvfs &fsstat)
{
map<dev_t,struct statvfs> fsstats;
unsigned long min_bsize = ULONG_MAX;
unsigned long min_frsize = ULONG_MAX;
unsigned long min_namemax = ULONG_MAX;
for(size_t i = 0, ei = srcmounts.size(); i < ei; i++)
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
_statfs_core(srcmounts[i].c_str(),min_bsize,min_frsize,min_namemax,fsstats);
_statfs_core(branches_[i].path,min_bsize,min_frsize,min_namemax,fsstats);
}
map<dev_t,struct statvfs>::iterator iter = fsstats.begin();
@ -135,9 +135,9 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _statfs(config.srcmounts,
return _statfs(config.branches,
*stat);
}
}

12
src/str.cpp

@ -147,4 +147,16 @@ namespace str
return ((s0.size() >= s1.size()) &&
(s0.compare(0,s1.size(),s1) == 0));
}
bool
ends_with(const string &str_,
const string &suffix_)
{
if(suffix_.size() > str_.size())
return false;
return std::equal(suffix_.rbegin(),
suffix_.rend(),
str_.rbegin());
}
}

4
src/str.hpp

@ -56,4 +56,8 @@ namespace str
bool
isprefix(const std::string &s0,
const std::string &s1);
bool
ends_with(const std::string &str_,
const std::string &suffix_);
}

10
src/symlink.cpp

@ -82,7 +82,7 @@ static
int
_symlink(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *oldpath,
const char *newpath)
@ -94,11 +94,11 @@ _symlink(Policy::Func::Search searchFunc,
newdirpath = fs::path::dirname(newpath);
rv = searchFunc(srcmounts,newdirpath,minfreespace,existingpaths);
rv = searchFunc(branches_,newdirpath,minfreespace,existingpaths);
if(rv == -1)
return -errno;
rv = createFunc(srcmounts,newdirpath,minfreespace,newbasepaths);
rv = createFunc(branches_,newdirpath,minfreespace,newbasepaths);
if(rv == -1)
return -errno;
@ -117,11 +117,11 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _symlink(config.getattr,
config.symlink,
config.srcmounts,
config.branches,
config.minfreespace,
oldpath,
newpath);

8
src/truncate.cpp

@ -71,7 +71,7 @@ _truncate_loop(const vector<const string*> &basepaths,
static
int
_truncate(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const off_t size)
@ -79,7 +79,7 @@ _truncate(Policy::Func::Action actionFunc,
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -97,10 +97,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _truncate(config.truncate,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
size);

8
src/unlink.cpp

@ -68,14 +68,14 @@ _unlink_loop(const vector<const string*> &basepaths,
static
int
_unlink(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath)
{
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -92,10 +92,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _unlink(config.unlink,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath);
}

8
src/utimens.cpp

@ -70,7 +70,7 @@ _utimens_loop(const vector<const string*> &basepaths,
static
int
_utimens(Policy::Func::Action actionFunc,
const vector<string> &srcmounts,
const Branches &branches_,
const uint64_t minfreespace,
const char *fusepath,
const timespec ts[2])
@ -78,7 +78,7 @@ _utimens(Policy::Func::Action actionFunc,
int rv;
vector<const string*> basepaths;
rv = actionFunc(srcmounts,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
if(rv == -1)
return -errno;
@ -96,10 +96,10 @@ namespace mergerfs
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock);
const rwlock::ReadGuard readlock(&config.branches_lock);
return _utimens(config.utimens,
config.srcmounts,
config.branches,
config.minfreespace,
fusepath,
ts);

18
src/write.cpp

@ -14,8 +14,6 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <fuse.h>
#include "config.hpp"
#include "errno.hpp"
#include "fileinfo.hpp"
@ -24,7 +22,14 @@
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <string>
#include <vector>
using namespace mergerfs;
using std::string;
using std::vector;
typedef int (*WriteFunc)(const int,const void*,const size_t,const off_t);
@ -96,10 +101,13 @@ namespace mergerfs
if(config.moveonenospc)
{
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.srcmountslock);
vector<string> paths;
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.branches_lock);
config.branches.to_paths(paths);
rv = fs::movefile(config.srcmounts,fi->fusepath,count,fi->fd);
rv = fs::movefile(paths,fi->fusepath,count,fi->fd);
if(rv == -1)
return -ENOSPC;

25
src/write_buf.cpp

@ -14,14 +14,6 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <fuse.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "config.hpp"
#include "errno.hpp"
#include "fileinfo.hpp"
@ -31,6 +23,14 @@
#include "ugid.hpp"
#include "write.hpp"
#include <fuse.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <vector>
using std::string;
using std::vector;
using namespace mergerfs;
@ -83,11 +83,14 @@ namespace mergerfs
if(config.moveonenospc)
{
size_t extra;
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.srcmountslock);
vector<string> paths;
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.branches_lock);
config.branches.to_paths(paths);
extra = fuse_buf_size(src);
rv = fs::movefile(config.srcmounts,fi->fusepath,extra,fi->fd);
rv = fs::movefile(paths,fi->fusepath,extra,fi->fd);
if(rv == -1)
return -ENOSPC;

Loading…
Cancel
Save