Compare commits

...
129 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 8bd3726cf8 fs_mknod_as.hpp 2025-12-29 20:20:28 -06:00
Antonio SJ Musumeci 18b72049a4 fs_mknod_as.hpp 2025-12-29 20:20:10 -06:00
Antonio SJ Musumeci 2f91276333 func_mknod_mfs.cpp 2025-12-29 20:18:27 -06:00
Antonio SJ Musumeci 7c1bea4757 func_mknod_mfs.cpp 2025-12-29 20:17:49 -06:00
Antonio SJ Musumeci 14f775a302 func_mknod_mfs.hpp 2025-12-29 20:17:20 -06:00
Antonio SJ Musumeci 4622bc8160 func_mknod_ff.hpp 2025-12-29 20:17:02 -06:00
Antonio SJ Musumeci 5176458c7b func_mknod.hpp 2025-12-29 20:16:20 -06:00
Antonio SJ Musumeci 06256e1120 func_mknod_base.hpp 2025-12-29 20:15:58 -06:00
Antonio SJ Musumeci ab27265347 func_mknod_base.hpp 2025-12-29 20:15:54 -06:00
Antonio SJ Musumeci 13b4bf4649 func_mknod_ff.cpp 2025-12-29 20:15:02 -06:00
Antonio SJ Musumeci dc5c7e95a6 func_mknod_ff.cpp 2025-12-29 20:14:50 -06:00
Antonio SJ Musumeci ef825e0ff6 func_mknod_ff.cpp 2025-12-29 20:14:32 -06:00
Antonio SJ Musumeci 557a02115c func_mknod_ff.cpp 2025-12-29 20:14:27 -06:00
Antonio SJ Musumeci 73c9d86481 func_mknod_ff.cpp 2025-12-29 20:14:06 -06:00
Antonio SJ Musumeci 7ba1b8f332 fs_mknod_as.hpp 2025-12-29 20:13:54 -06:00
Antonio SJ Musumeci 31470ee458 fs_mknod_as.hpp 2025-12-29 20:13:53 -06:00
Antonio SJ Musumeci 0ef62afcbf fs_mknod_as.hpp 2025-12-29 20:13:52 -06:00
Antonio SJ Musumeci 2cd64960e3 fs_mknod_as.hpp 2025-12-29 20:13:50 -06:00
Antonio SJ Musumeci fb534e5aba fs_mknod_as.hpp 2025-12-29 20:13:28 -06:00
Antonio SJ Musumeci 6e4b618376 fs_mknod_as.hpp 2025-12-29 20:13:00 -06:00
Antonio SJ Musumeci 802f9a0eaf fs_mknod_as.hpp 2025-12-29 20:12:53 -06:00
Antonio SJ Musumeci 04f2068adf fs_mknod_as.hpp 2025-12-29 20:11:58 -06:00
Antonio SJ Musumeci d36688c752 func_mknod_ff.cpp 2025-12-29 20:07:18 -06:00
Antonio SJ Musumeci e0cf997312 fuse_mknod.cpp 2025-12-29 20:03:43 -06:00
Antonio SJ Musumeci 999b774dcb tests.cpp 2025-12-29 19:53:22 -06:00
Antonio SJ Musumeci c0252fa5e6 tests.cpp 2025-12-29 19:52:57 -06:00
Antonio SJ Musumeci 50caf408f6 tests.cpp 2025-12-29 19:52:56 -06:00
Antonio SJ Musumeci 2fcd019f30 tests.cpp 2025-12-29 19:48:45 -06:00
Antonio SJ Musumeci 3fed8a12db tests.cpp 2025-12-29 19:48:27 -06:00
Antonio SJ Musumeci 17f7df7b3e tests.cpp 2025-12-29 19:48:14 -06:00
Antonio SJ Musumeci 0b552d11f8 func_mknod_ff.cpp 2025-12-29 19:45:25 -06:00
Antonio SJ Musumeci c59c79f92c func_mknod_ff.cpp 2025-12-29 19:45:21 -06:00
Antonio SJ Musumeci 2e1d028516 tests.cpp 2025-12-29 19:40:47 -06:00
Antonio SJ Musumeci 316c971e6f fs_xattr.cpp 2025-12-29 19:35:50 -06:00
Antonio SJ Musumeci ee4a8a18ac fs_xattr.cpp 2025-12-29 19:32:21 -06:00
Antonio SJ Musumeci 4675d8045b tests.cpp 2025-12-29 19:24:45 -06:00
Antonio SJ Musumeci 72fde7def8 tests.cpp 2025-12-29 19:22:24 -06:00
Antonio SJ Musumeci 7e8e279ed3 tests.cpp 2025-12-29 19:21:59 -06:00
Antonio SJ Musumeci b021e73752 tests.cpp 2025-12-29 19:21:53 -06:00
Antonio SJ Musumeci 06f0d1b2d8 tests.cpp 2025-12-29 19:21:30 -06:00
Antonio SJ Musumeci 7eeee57043 tests.cpp 2025-12-29 19:20:47 -06:00
Antonio SJ Musumeci 21b8e376be tests.cpp 2025-12-29 19:16:35 -06:00
Antonio SJ Musumeci 353a5f7103 tests.cpp 2025-12-29 19:16:34 -06:00
Antonio SJ Musumeci a863981746 tests.cpp 2025-12-29 19:15:48 -06:00
Antonio SJ Musumeci 71a939112d tests.cpp 2025-12-29 19:15:17 -06:00
Antonio SJ Musumeci 882634fa38 tests.cpp 2025-12-29 19:14:31 -06:00
Antonio SJ Musumeci 03a28d3ebe tests.cpp 2025-12-29 19:14:20 -06:00
Antonio SJ Musumeci 6e5b4aa40f tests.cpp 2025-12-29 19:14:03 -06:00
Antonio SJ Musumeci 28c36aea0e func_mknod_ff.cpp 2025-12-29 19:12:25 -06:00
Antonio SJ Musumeci bcb45b917d func_mknod_ff.cpp 2025-12-29 19:11:51 -06:00
Antonio SJ Musumeci 59bec051fc func_mknod_ff.cpp 2025-12-29 19:10:43 -06:00
Antonio SJ Musumeci 9dba189b90 func_mknod_ff.cpp 2025-12-29 19:10:23 -06:00
Antonio SJ Musumeci e8289f2cbe func_mknod_ff.cpp 2025-12-29 19:09:42 -06:00
Antonio SJ Musumeci ea88c8ac56 func_mknod_ff.cpp 2025-12-29 19:09:36 -06:00
Antonio SJ Musumeci 2c177ddaeb func_mknod_ff.cpp 2025-12-29 18:12:27 -06:00
Antonio SJ Musumeci bb674484b1 func_mknod_ff.cpp 2025-12-29 18:12:09 -06:00
Antonio SJ Musumeci 3f0ec90891 func_mknod_ff.cpp 2025-12-29 18:11:58 -06:00
Antonio SJ Musumeci 2338e7d8a9 func_mknod_ff.cpp 2025-12-29 18:10:56 -06:00
Antonio SJ Musumeci 8ec2d01d59 func_mknod_ff.cpp 2025-12-29 18:10:53 -06:00
Antonio SJ Musumeci 4996628e7f func_mknod_ff.cpp 2025-12-29 18:10:47 -06:00
Antonio SJ Musumeci 3c96b138f0 func_mknod_ff.cpp 2025-12-29 18:10:43 -06:00
Antonio SJ Musumeci 4d81f824a4 func_mknod_ff.cpp 2025-12-29 18:10:37 -06:00
Antonio SJ Musumeci d90763fcd2 func_mknod_ff.cpp 2025-12-29 18:10:30 -06:00
Antonio SJ Musumeci b3100540c6 func_mknod_ff.cpp 2025-12-29 18:10:02 -06:00
Antonio SJ Musumeci 5db592d4db func_mknod_ff.cpp 2025-12-29 18:09:57 -06:00
Antonio SJ Musumeci 695b1042f9 func_mknod_ff.cpp 2025-12-29 18:09:16 -06:00
Antonio SJ Musumeci fe4c7faf4b func_mknod_ff.cpp 2025-12-29 18:09:12 -06:00
Antonio SJ Musumeci da13bdff15 func_mknod_ff.cpp 2025-12-29 18:09:12 -06:00
Antonio SJ Musumeci 5b478446aa func_mknod_ff.cpp 2025-12-29 18:09:10 -06:00
Antonio SJ Musumeci a376263d43 func_mknod_ff.cpp 2025-12-29 18:08:30 -06:00
Antonio SJ Musumeci a0e10d7638 func_mknod_ff.cpp 2025-12-29 18:06:26 -06:00
Antonio SJ Musumeci f5bb8f6c60 func_mknod_ff.cpp 2025-12-29 18:06:21 -06:00
Antonio SJ Musumeci 266a936208 func_mknod_ff.cpp 2025-12-29 18:06:16 -06:00
Antonio SJ Musumeci 15dd1cae2b func_mknod_ff.cpp 2025-12-29 18:06:08 -06:00
Antonio SJ Musumeci 867fb8ef46 func_mknod_ff.cpp 2025-12-29 18:06:05 -06:00
Antonio SJ Musumeci b33a72ca5c func_mknod_ff.cpp 2025-12-29 13:48:50 -06:00
Antonio SJ Musumeci 2fa854dcc3 func_mknod_ff.cpp 2025-12-29 13:48:40 -06:00
Antonio SJ Musumeci 03145a0789 func_mknod_ff.cpp 2025-12-29 13:48:35 -06:00
Antonio SJ Musumeci a2638a98b0 func_mknod_ff.cpp 2025-12-29 11:35:20 -06:00
Antonio SJ Musumeci 8188b13d73 func_mknod_ff.cpp 2025-12-29 11:25:40 -06:00
Antonio SJ Musumeci 46d3297d5e func_mknod_ff.cpp 2025-12-29 11:25:10 -06:00
Antonio SJ Musumeci 51af59ee59 func_mknod_ff.cpp 2025-12-29 11:25:07 -06:00
Antonio SJ Musumeci 8a961d56d9 func_mknod_ff.cpp 2025-12-29 11:24:54 -06:00
Antonio SJ Musumeci 2166f3537b func_mknod_ff.cpp 2025-12-29 11:24:08 -06:00
Antonio SJ Musumeci 8e0e88cd34 func_mknod_ff.cpp 2025-12-29 11:21:02 -06:00
Antonio SJ Musumeci 02cb3e2242 func_mknod_ff.cpp 2025-12-29 11:20:56 -06:00
Antonio SJ Musumeci 4b02d1c6c0 func_mknod_ff.cpp 2025-12-29 11:20:54 -06:00
Antonio SJ Musumeci 89bbd38ef2 func_mknod_ff.cpp 2025-12-29 11:20:50 -06:00
Antonio SJ Musumeci 4b8d68fb03 func_mknod_ff.cpp 2025-12-29 11:12:19 -06:00
Antonio SJ Musumeci 234f534204 fuse_mknod.cpp 2025-12-29 11:00:37 -06:00
Antonio SJ Musumeci 08b11ea09d fuse_mknod.cpp 2025-12-29 11:00:36 -06:00
Antonio SJ Musumeci 1b1d46a35c fuse_mknod.cpp 2025-12-29 11:00:32 -06:00
Antonio SJ Musumeci 07db8d2ee9 fuse_mknod.cpp 2025-12-29 11:00:03 -06:00
Antonio SJ Musumeci 332ca520aa fuse_mknod.cpp 2025-12-29 10:59:57 -06:00
Antonio SJ Musumeci e18fcc5714 fuse_mknod.cpp 2025-12-29 10:59:46 -06:00
Antonio SJ Musumeci d818892b93 fuse_mknod.cpp 2025-12-29 10:59:35 -06:00
Antonio SJ Musumeci 8ab6bcf554 checkpoint 2025-12-29 10:58:37 -06:00
Antonio SJ Musumeci b3b8e793f9 Change getattr and statx to bespoke policies
* Provides: slight improvement in performance as it no longer will
need to find and then act on the files.
* It doesn't make sense to support all original policy types. Most
people used 'newest' or 'ff' and 'combine' is a better default which
has same runtime as 'newest' but with better output.
2025-12-27 22:14:56 -06:00
trapexit 965e26989c Misc documentation updates (#1605) 2025-12-23 23:25:39 -06:00
trapexit 0caae4fbf0 Tag container image to ghcr for convenience (#1604) 2025-12-14 15:21:56 -06:00
trapexit 4d1760cb69 Rework new lup policy to work on 32bit systems (#1603)
__int128 is not available on 32bit platforms. Moved to extended
precision floats.
2025-12-14 14:56:34 -06:00
François-Xavier Payet 22df87c86a Add LUP policy (#1601) 2025-12-14 00:36:12 -06:00
trapexit 72fb6e4aba Update submitting bug reports, improve collect-info (#1598) 2025-12-06 13:55:14 -06:00
trapexit 9b046d9a34 Rework credential handling to support chroot & idmap (#1595)
Run in an elevated credential mode (root) and let the kernel manage
entitlements. Will result in slightly different behavior but should
not be noticed by most.
2025-12-05 21:03:48 -06:00
Emanuel Haupt d8918458bf libfuse: fix narrowing conversion of nsec fields on i386 (#1593)
On 32 bit platforms tv_nsec is a signed long while the FUSE setattr
arguments provide nanoseconds as uint32_t. C++17 treats uint32_t -> long
in a brace initializer as a narrowing conversion and rejects it on ILP32
targets. This surfaced when building on FreeBSD i386 with clang 19 where
the compiler refused the list initialization of struct timespec.

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

* Fix static builds
2025-11-01 15:20:30 -05:00
trapexit ec16d07fba Mention how mmap error when direct io is enabled (#1557) 2025-10-31 09:31:37 -05:00
trapexit 37f10888a9 Add more debugging options to makefiles and cleanup (#1556) 2025-10-29 22:42:42 -05:00
Satadru Pramanik, DO, MPH, MEng 0db8a1ed6a Fix manpage links for 2.41.0-rc3 (#1555) 2025-10-29 12:07:49 -05:00
286 changed files with 7078 additions and 4624 deletions
-150
View File
@@ -1,150 +0,0 @@
linux_task:
name: "alpine:latest"
container:
image: alpine:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "alpine-arm:latest"
arm_container:
image: alpine:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make STATIC=1 LTO=1
linux_task:
name: "fedora:41"
container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "fedora-arm:41"
arm_container:
image: fedora:41
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- make
- make rpm
linux_task:
name: "ubuntu:22.04"
container:
image: ubuntu:22.04
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu:24.04"
container:
image: ubuntu:24.04
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu:latest"
container:
image: ubuntu:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "ubuntu-arm:latest"
arm_container:
image: ubuntu:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:12"
container:
image: debian:12
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian:latest"
container:
image: debian:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
linux_task:
name: "debian-arm:latest"
arm_container:
image: debian:latest
cpu: 4
memory: 4G
timeout_in: 15m
script:
- buildtools/install-build-pkgs
- git fetch
- make deb
- apt-get -y install fuse
- dpkg -i ../*.deb
- mergerfs -v || true
+44 -16
View File
@@ -7,22 +7,31 @@ assignees: ''
---
See also https://trapexit.github.io/mergerfs/latest/support
**Describe the bug**
A clear and concise description of the unexpected behavior.
**Please be sure to use latest release of mergerfs to ensure the issue still exists. Not your distro's latest but the latest official release.**
**Please be sure to use latest release of mergerfs to ensure the issue
still exists. Not your distro's latest but the latest official
release. Support will NOT be provided for old versions.**
The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
The `master` branch is **not** to be considered production ready. Feel
free to file bug reports but do so indicating clearly that you are
testing unreleased code.
**To Reproduce**
Steps to reproduce the behavior. List **all** steps to reproduce. **All** settings.
Provide steps to reproduce the behavior. List **all** steps to
reproduce. **All** settings. The version of all software involved.
Please simplify the reproduction as much as possible.
- Unless it is dependenat on multiple branches, use a single branch
- Reproduce with standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party software will make debugging more difficult.
* Unless it is dependenat on multiple branches, use a single branch
* Reproduce with standard tooling if possible
(touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party
software will make debugging more difficult.
**Expected behavior**
@@ -30,19 +39,38 @@ Please simplify the reproduction as much as possible.
A clear and concise description of the expected behavior.
**Workarounds already tested**
Please list all workarounds attempted and why they were ruled out as
options.
**System information:**
- OS, kernel version: `uname -a`
- mergerfs version: `mergerfs -V`
- mergerfs settings
- List of drives, filesystems, & sizes:
- `df -h`
- `lsblk -f`
- A strace of the application having a problem:
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
- strace of mergerfs while app tried to do it's thing:
- `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
* Attach output from
[mergerfs.collect-info](https://trapexit.github.io/mergerfs/latest/tooling/#mergerfscollect-info)
tool.
* Alternatively:
* OS: `lsb_release -a`
* kernel version: `uname -a`
* mergerfs version: `mergerfs --version`
* mergerfs settings / arguments: from fstab, systemd unit, command
line, OMV plugin, etc.
* List of drives, filesystems, & sizes:
* `df -h`
* `lsblk -o NAME,FSTYPE,FSSIZE,SIZE,MOUNTPOINTS,RM,RO,ROTA`
* All information about the relevant paths and files: permissions,
ownership, etc.
* All information about the application making the requests:
version, uid/gid
* Is mergerfs running within a container or used with container
technologies?
* Are the client apps using mergerfs running in a container?
* A strace of the application having a problem:
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
* strace of mergerfs while app tried to do it's thing:
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
**Additional context**
-42
View File
@@ -1,42 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "7 3 * * 6"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python, cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
if: ${{ matrix.language == 'python' || matrix.language == 'cpp' }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
+2 -11
View File
@@ -29,7 +29,7 @@ jobs:
- name: Install dependencies
working-directory: ./mkdocs
run: |
pip install --no-cache-dir mkdocs mkdocs-material pymdown-extensions mike
./build-venv
- name: Deploy docs
working-directory: ./mkdocs
@@ -37,13 +37,4 @@ jobs:
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
run: |
if [ "$REF_TYPE" = "tag" ]; then
mike deploy "$REF_NAME" "latest" --allow-empty --ignore-remote-status --update-aliases
mike set-default "latest"
#git checkout gh-pages
#git push -f origin gh-pages
elif [ "$REF_TYPE" = "branch" ] && [ "$REF_NAME" = "master" ]; then
mike deploy "master" --ignore-remote-status --allow-empty
#git checkout gh-pages
#git push -f origin gh-pages
fi
./generate-and-push-docs
+30 -3
View File
@@ -12,6 +12,8 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
$(info mergerfs MAKEFLAGS: $(MAKEFLAGS))
ifeq ($(shell id -u),0)
FAKEROOT ?=
endif
@@ -34,6 +36,7 @@ TOUCH ?= touch
BUILDDIR := build
GITHUB_REPO ?= "https://github.com/trapexit/mergerfs"
RELEASE_SAMPLE ?= "debian:13.amd64"
ifndef GIT_REPO
ifneq ($(shell $(GIT) --version 2> /dev/null),)
@@ -49,7 +52,21 @@ UGID_USE_RWLOCK ?= 0
ifdef NDEBUG
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O0 -g -fno-omit-frame-pointer -DDEBUG
ifdef SANITIZE
ifeq ($(SANITIZE),1)
override SANITIZE := -fsanitize=address,undefined,leak
else
override SANITIZE := -fsanitize=$(SANITIZE)
endif
endif
OPT_FLAGS := -O0 \
-g \
-fno-omit-frame-pointer \
$(SANITIZE) \
-fstack-protector-strong \
-Wextra \
-Wno-unused-parameter \
-DDEBUG
endif
ifdef STATIC
@@ -172,7 +189,7 @@ $(BUILDDIR)/tests: $(BUILDDIR)/mergerfs $(TESTS_OBJS)
.PHONY: libfuse
$(LIBFUSE):
libfuse:
$(MAKE) NDEBUG=$(NDEBUG) -C libfuse
$(MAKE) -C libfuse
tests: $(BUILDDIR)/tests
@@ -276,6 +293,10 @@ ifdef GIT_REPO
$(GIT2DEBCL) --name mergerfs > debian/changelog
else
$(CP) -v ChangeLog debian/changelog
$(eval DISTRO := $(shell lsb_release -i -s | tr '[:upper:]' '[:lower:]'))
$(eval CODENAME := $(shell lsb_release -c -s | tr '[:upper:]' '[:lower:]'))
sed -i 's/~unknown-unknown/~$(DISTRO)-$(CODENAME)/g' debian/changelog
sed -i 's/ unknown; / $(CODENAME); /g' debian/changelog
endif
.PHONY: signed-deb
@@ -318,6 +339,7 @@ install-build-tools:
define build_release
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
@echo "GITREF=$(GITREF)"
./buildtools/build-release \
--target=$(1) \
$(if $(CLEANUP),--cleanup) \
@@ -332,7 +354,7 @@ endef
release:
$(call build_release,"all")
release-sample:
$(call build_release,"debian:13.amd64")
$(call build_release,$(RELEASE_SAMPLE))
release-amd64:
$(call build_release,"amd64")
release-arm64:
@@ -346,6 +368,11 @@ release-static:
release-tarball:
$(call build_release,"tarball")
container:
$(eval GITREF ?= $(shell git describe --exact-match --tags HEAD 2>/dev/null || git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD))
@echo "GITREF=$(GITREF)"
./buildtools/build-containerimage "$(GITREF)"
.PHONY: tags
tags:
rm -fv TAGS
+1 -1
View File
@@ -25,7 +25,7 @@ etc.](https://trapexit.github.io/mergerfs/latest/project_comparisons/)
* Works with filesystems of [almost any
type](https://trapexit.github.io/mergerfs/latest/faq/compatibility_and_integration/#what-filesystems-can-be-used-as-branches)
* Ignore read-only filesystems when creating files
* [Hard links](https://trapexit.github.io/mergerfs/preview/faq/technical_behavior_and_limitations/#do-hard-links-work)
* [Hard links](https://trapexit.github.io/mergerfs/latest/faq/technical_behavior_and_limitations/#do-hard-links-work)
* Hard link [copy-on-write /
CoW](https://trapexit.github.io/mergerfs/latest/config/link_cow/)
* [Runtime configurable](https://trapexit.github.io/mergerfs/latest/runtime_interface/)
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
GIT_BRANCH="${1:-master}"
set -x
podman \
build \
--squash \
--no-cache \
--force-rm \
--build-arg="GIT_REPO=file:///mnt" \
--build-arg="BRANCH=${GIT_BRANCH}" \
-v "${PWD}":/mnt:ro \
-f "buildtools/containerimage/Containerfile" \
--tag "ghcr.io/trapexit/mergerfs:${GIT_BRANCH}" \
buildtools/
+1 -1
View File
@@ -19,7 +19,7 @@ def build(git_repo,
if os.path.exists(git_repo):
git_repo = os.path.realpath(git_repo)
args += ['-v',f'{git_repo}:/tmp/mergerfs.git:ro']
args += ['--build-arg=GIT_REPO=/tmp/mergerfs.git']
args += ['--build-arg=GIT_REPO=file:///tmp/mergerfs.git']
else:
args += [f'--build-arg=GIT_REPO={git_repo}']
args += ['-o',pkgdirpath,
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/amd64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_amd64.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_amd64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/arm64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_arm64.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_arm64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/armhf alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_armhf.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_armhf.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/i386 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_i386.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_i386.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+6 -4
View File
@@ -1,13 +1,15 @@
ARG BUILD_TIMESTAMP=0
FROM --platform=linux/riscv64 alpine:latest as build
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN git clone --single-branch --branch="${BRANCH}" ${GIT_REPO} /tmp/mergerfs
WORKDIR /tmp/mergerfs
RUN make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp -j$(nproc) install
RUN mkdir /build
RUN tar cvfz /build/mergerfs-static-linux_riscv64.tar.gz --directory=/tmp usr sbin
RUN /tmp/usr/local/bin/mergerfs --version | head -n1 | cut -c 11- | tee /tmp/version
RUN tar cvfz /build/mergerfs-$(cat /tmp/version)-static-linux_riscv64.tar.gz --directory=/tmp usr sbin
FROM scratch
COPY --from=build /build/ /
+7 -3
View File
@@ -1,13 +1,17 @@
ARG BUILD_TIMESTAMP=0
FROM alpine:latest as tarball
COPY install-build-pkgs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG BRANCH=master
RUN git clone --single-branch --depth=1 https://github.com/trapexit/mergerfs /tmp/mergerfs --branch="${BRANCH}"
ARG GIT_REPO
RUN git clone --single-branch "${GIT_REPO}" /tmp/mergerfs --branch="${BRANCH}"
RUN cd /tmp/mergerfs && make version tarball
RUN mkdir /build
RUN cp -v /tmp/mergerfs/*.tar.gz /build/
FROM scratch
COPY --from=tarball /build/ /
@@ -0,0 +1,10 @@
FROM --platform=amd64 ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=arm64 ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=armhf ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
@@ -0,0 +1,10 @@
FROM --platform=riscv64 ubuntu:25.10 as build
COPY install-build-pkgs build-mergerfs /tmp/
RUN /tmp/install-build-pkgs
ARG BUILD_TIMESTAMP=0
ARG GIT_REPO
ARG BRANCH
RUN /tmp/build-mergerfs $GIT_REPO $BRANCH
FROM scratch
COPY --from=build /build/ /
+19
View File
@@ -0,0 +1,19 @@
FROM --platform=linux/amd64 alpine:latest as build
ARG GIT_REPO
ARG BRANCH
RUN <<EOF
apk add git gcc g++ make linux-headers
git clone --single-branch --branch="${BRANCH}" "${GIT_REPO}" /tmp/mergerfs
cd /tmp/mergerfs
make NDEBUG=1 LTO=1 STATIC=1 DESTDIR=/tmp/install -j$(nproc) install
tar cvfz /tmp/mergerfs.tgz --directory=/tmp/install usr sbin
EOF
FROM --platform=linux/amd64 alpine:latest
RUN --mount=from=build,source=/tmp/mergerfs.tgz,target=/tmp/mergerfs.tgz <<EOF
tar xvf /tmp/mergerfs.tgz -C /
EOF
ENTRYPOINT ["/usr/local/bin/mergerfs"]
CMD ["--help"]
+27 -2
View File
@@ -12,6 +12,8 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
$(info libfuse MAKEFLAGS: $(MAKEFLAGS))
AR ?= ar
CHMOD ?= chmod
CHOWN ?= chown
@@ -33,7 +35,22 @@ endif
ifeq ($(NDEBUG),1)
OPT_FLAGS := -O2 -DNDEBUG
else
OPT_FLAGS := -O0 -g -DDEBUG -fno-omit-frame-pointer
ifdef SANITIZE
ifeq ($(SANITIZE),1)
override SANITIZE := -fsanitize=address,undefined,leak
else
override SANITIZE := -fsanitize=$(SANITIZE)
endif
endif
OPT_FLAGS := -O0 \
-g \
-fno-omit-frame-pointer \
$(SANITIZE) \
-fstack-protector-strong \
-Wextra \
-Werror \
-Wno-unused-parameter \
-DDEBUG
endif
ifeq ($(LTO),1)
@@ -60,7 +77,6 @@ INSTALLMAN1DIR ?= $(DESTDIR)$(MAN1DIR)
BUILDDIR := build
SRC_C = \
lib/buffer.c \
lib/crc32b.c \
lib/fuse_opt.c \
lib/fuse_session.c \
@@ -74,10 +90,17 @@ OBJS += $(SRC_CXX:lib/%.cpp=$(BUILDDIR)/.objs/%.cpp.o)
DEPS := $(SRC_C:lib/%.c=$(BUILDDIR)/.objs/%.c.d)
DEPS += $(SRC_CXX:lib/%.cpp=$(BUILDDIR)/.objs/%.cpp.d)
ifdef STATIC
STATIC_FLAGS := -static
else
STATIC_FLAGS :=
endif
CPPFLAGS ?=
CFLAGS ?= \
$(OPT_FLAGS) \
$(LTO_FLAGS) \
$(STATIC_FLAGS) \
-Wall \
-pipe
override CFLAGS += \
@@ -87,6 +110,7 @@ override CFLAGS += \
CXXFLAGS ?= \
$(OPT_FLAGS) \
$(LTO_FLAGS) \
$(STATIC_FLAGS) \
-Wall \
-pipe
override CXXFLAGS += \
@@ -124,6 +148,7 @@ $(BUILDDIR)/mergerfs-fusermount: util/fusermount.c lib/mount_util.c
mergerfs-fusermount: $(BUILDDIR)/mergerfs-fusermount
$(BUILDDIR)/mount.mergerfs: $(BUILDDIR)/libfuse.a util/mount.mergerfs.c
echo STATIC=$(STATIC) $(STATIC_FLAGS)
$(CC) $(CFLAGS) $(FUSE_FLAGS) -o $(BUILDDIR)/mount.mergerfs util/mount.mergerfs.c $(BUILDDIR)/libfuse.a $(LDFLAGS)
mount.mergerfs: $(BUILDDIR)/mount.mergerfs
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
typedef const uint32_t cu32;
typedef const int32_t cs32;
typedef const uint64_t cu64;
typedef const int64_t cs64;
typedef float f32;
typedef double f64;
typedef long double f80;
typedef char assertion_f32_is_32bit[sizeof(f32) == 4 ? 1 : -1];
typedef char assertion_f64_is_64bit[sizeof(f64) == 8 ? 1 : -1];
typedef char assertion_f80_is_at_least_64bit[sizeof(f64) >= 8 ? 1 : -1];
typedef const float cf32;
typedef const double cf64;
typedef const long double cf80;
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
#include <climits>
#include <string>
-64
View File
@@ -330,70 +330,6 @@ struct fuse_buf {
off_t pos;
};
/**
* Data buffer vector
*
* An array of data buffers, each containing a memory pointer or a
* file descriptor.
*
* Allocate dynamically to add more than one buffer.
*/
struct fuse_bufvec {
/**
* Number of buffers in the array
*/
size_t count;
/**
* Index of current buffer within the array
*/
size_t idx;
/**
* Current offset within the current buffer
*/
size_t off;
/**
* Array of buffers
*/
struct fuse_buf buf[1];
};
/* Initialize bufvec with a single buffer of given size */
#define FUSE_BUFVEC_INIT(size__) \
((struct fuse_bufvec) { \
/* .count= */ 1, \
/* .idx = */ 0, \
/* .off = */ 0, \
/* .buf = */ { /* [0] = */ { \
/* .size = */ (size__), \
/* .flags = */ (enum fuse_buf_flags) 0, \
/* .mem = */ NULL, \
/* .fd = */ -1, \
/* .pos = */ 0, \
} } \
} )
/**
* Get total size of data in a fuse buffer vector
*
* @param bufv buffer vector
* @return size of data
*/
size_t fuse_buf_size(const struct fuse_bufvec *bufv);
/**
* Copy data from one buffer vector to another
*
* @param dst destination buffer vector
* @param src source buffer vector
* @param flags flags controlling the copy
* @return actual number of bytes copied or -errno on error
*/
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
enum fuse_buf_copy_flags flags);
/* ----------------------------------------------------------- *
* Signal handling *
* ----------------------------------------------------------- */
-24
View File
@@ -472,30 +472,6 @@ int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
uint64_t parent, uint64_t child,
const char *name, size_t namelen);
/**
* Store data to the kernel buffers
*
* Synchronously store data in the kernel buffers belonging to the
* given inode. The stored data is marked up-to-date (no read will be
* performed against it, unless it's invalidated or evicted from the
* cache).
*
* If the stored data overflows the current file size, then the size
* is extended, similarly to a write(2) on the filesystem.
*
* If this function returns an error, then the store wasn't fully
* completed, but it may have been partially completed.
*
* @param ch the channel through which to send the invalidation
* @param ino the inode number
* @param offset the starting offset into the file to store to
* @param bufv buffer vector
* @param flags flags controlling the copy
* @return zero for success, -errno for failure
*/
int fuse_lowlevel_notify_store(struct fuse_chan *ch, uint64_t ino,
off_t offset, struct fuse_bufvec *bufv,
enum fuse_buf_copy_flags flags);
/**
* Retrieve data from the kernel buffers
*
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
#include "fuse_msgbuf_t.h"
u32 msgbuf_get_pagesize();
-13
View File
@@ -1,13 +0,0 @@
#pragma once
#include <stdint.h>
typedef uint64_t u32;
typedef int64_t s32;
typedef uint64_t u64;
typedef int64_t s64;
typedef const uint64_t cu32;
typedef const int64_t cs32;
typedef const uint64_t cu64;
typedef const int64_t cs64;
-316
View File
@@ -1,316 +0,0 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2010 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB
*/
#define _GNU_SOURCE
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
{
size_t i;
size_t size = 0;
for (i = 0; i < bufv->count; i++) {
if (bufv->buf[i].size == SIZE_MAX)
size = SIZE_MAX;
else
size += bufv->buf[i].size;
}
return size;
}
static size_t min_size(size_t s1, size_t s2)
{
return s1 < s2 ? s1 : s2;
}
static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
ssize_t res = 0;
size_t copied = 0;
while (len) {
if (dst->flags & FUSE_BUF_FD_SEEK) {
res = pwrite(dst->fd, src->mem + src_off, len,
dst->pos + dst_off);
} else {
res = write(dst->fd, src->mem + src_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
copied += res;
if (!(dst->flags & FUSE_BUF_FD_RETRY))
break;
src_off += res;
dst_off += res;
len -= res;
}
return copied;
}
static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
ssize_t res = 0;
size_t copied = 0;
while (len) {
if (src->flags & FUSE_BUF_FD_SEEK) {
res = pread(src->fd, dst->mem + dst_off, len,
src->pos + src_off);
} else {
res = read(src->fd, dst->mem + dst_off, len);
}
if (res == -1) {
if (!copied)
return -errno;
break;
}
if (res == 0)
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY))
break;
dst_off += res;
src_off += res;
len -= res;
}
return copied;
}
static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len)
{
char buf[4096];
struct fuse_buf tmp = {
.size = sizeof(buf),
.flags = 0,
};
ssize_t res;
size_t copied = 0;
tmp.mem = buf;
while (len) {
size_t this_len = min_size(tmp.size, len);
size_t read_len;
res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
read_len = res;
res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
if (res < 0) {
if (!copied)
return res;
break;
}
if (res == 0)
break;
copied += res;
if (res < this_len)
break;
dst_off += res;
src_off += res;
len -= res;
}
return copied;
}
#ifdef HAVE_SPLICE
static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
int splice_flags = 0;
off_t *srcpos = NULL;
off_t *dstpos = NULL;
off_t srcpos_val;
off_t dstpos_val;
ssize_t res;
size_t copied = 0;
if (flags & FUSE_BUF_SPLICE_MOVE)
splice_flags |= SPLICE_F_MOVE;
if (flags & FUSE_BUF_SPLICE_NONBLOCK)
splice_flags |= SPLICE_F_NONBLOCK;
if (src->flags & FUSE_BUF_FD_SEEK) {
srcpos_val = src->pos + src_off;
srcpos = &srcpos_val;
}
if (dst->flags & FUSE_BUF_FD_SEEK) {
dstpos_val = dst->pos + dst_off;
dstpos = &dstpos_val;
}
while (len) {
res = splice(src->fd, srcpos, dst->fd, dstpos, len, splice_flags);
if (res == -1) {
if (copied)
break;
if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
return -errno;
/* Maybe splice is not supported for this combination */
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
len);
}
if (res == 0)
break;
copied += res;
if (!(src->flags & FUSE_BUF_FD_RETRY) &&
!(dst->flags & FUSE_BUF_FD_RETRY)) {
break;
}
len -= res;
}
return copied;
}
#else
static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
(void) flags;
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
}
#endif
static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
const struct fuse_buf *src, size_t src_off,
size_t len, enum fuse_buf_copy_flags flags)
{
int src_is_fd = src->flags & FUSE_BUF_IS_FD;
int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
if (!src_is_fd && !dst_is_fd) {
char *dstmem = dst->mem + dst_off;
char *srcmem = src->mem + src_off;
if (dstmem != srcmem) {
if (dstmem + len <= srcmem || srcmem + len <= dstmem)
memcpy(dstmem, srcmem, len);
else
memmove(dstmem, srcmem, len);
}
return len;
} else if (!src_is_fd) {
return fuse_buf_write(dst, dst_off, src, src_off, len);
} else if (!dst_is_fd) {
return fuse_buf_read(dst, dst_off, src, src_off, len);
} else if (flags & FUSE_BUF_NO_SPLICE) {
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
} else {
return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
}
}
static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
{
if (bufv->idx < bufv->count)
return &bufv->buf[bufv->idx];
else
return NULL;
}
static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
{
const struct fuse_buf *buf = fuse_bufvec_current(bufv);
bufv->off += len;
assert(bufv->off <= buf->size);
if (bufv->off == buf->size) {
assert(bufv->idx < bufv->count);
bufv->idx++;
if (bufv->idx == bufv->count)
return 0;
bufv->off = 0;
}
return 1;
}
ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
enum fuse_buf_copy_flags flags)
{
size_t copied = 0;
if (dstv == srcv)
return fuse_buf_size(dstv);
for (;;) {
const struct fuse_buf *src = fuse_bufvec_current(srcv);
const struct fuse_buf *dst = fuse_bufvec_current(dstv);
size_t src_len;
size_t dst_len;
size_t len;
ssize_t res;
if (src == NULL || dst == NULL)
break;
src_len = src->size - srcv->off;
dst_len = dst->size - dstv->off;
len = min_size(src_len, dst_len);
res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
if (res < 0) {
if (!copied)
return res;
break;
}
copied += res;
if (!fuse_bufvec_advance(srcv, res) ||
!fuse_bufvec_advance(dstv, res))
break;
if (res < len)
break;
}
return copied;
}
+1 -1
View File
@@ -78,8 +78,8 @@ crc32b_continue(const void *buf_,
const crc32b_t len_,
const crc32b_t crc_)
{
int i;
char *buf;
crc32b_t i;
crc32b_t crc;
crc = crc_;
+45 -32
View File
@@ -275,7 +275,9 @@ static
void
debug_open_flags(const uint32_t flags_)
{
fmt::print("{}, ",open_accmode_to_str(flags_));
fmt::print(g_OUTPUT,
"{}, ",
open_accmode_to_str(flags_));
for(size_t i = 0; i < (sizeof(flags_) * 8); i++)
{
const char *str;
@@ -287,7 +289,7 @@ debug_open_flags(const uint32_t flags_)
if(str == NULL)
continue;
fmt::print("{}, ",str);
fmt::print(g_OUTPUT,"{}, ",str);
}
}
@@ -315,7 +317,7 @@ fuse_fopen_flag_to_str(const uint32_t offset_)
void
debug_fuse_open_out(const struct fuse_open_out *arg_)
{
fmt::print(stderr,
fmt::print(g_OUTPUT,
"fuse_open_out:"
" fh=0x{:#08x};"
" open_flags=0x{:#04x} (",
@@ -332,9 +334,9 @@ debug_fuse_open_out(const struct fuse_open_out *arg_)
if(str == NULL)
continue;
fmt::print(stderr,"{},",str);
fmt::print(g_OUTPUT,"{},",str);
}
fmt::print(stderr,");\n");
fmt::print(g_OUTPUT,");\n");
}
static
@@ -445,7 +447,7 @@ debug_fuse_mkdir_in(const void *arg_)
"fuse_mkdir_in:"
" mode={:o};"
" umask={:o};"
" name=%s;"
" name={};"
"\n"
,
arg->mode,
@@ -461,7 +463,7 @@ debug_fuse_unlink(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_unlink:"
" name=%s;"
" name={};"
"\n"
,
name);
@@ -475,7 +477,7 @@ debug_fuse_rmdir(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_mkdir:"
" name=%s;"
" name={};"
"\n"
,
name);
@@ -493,8 +495,8 @@ debug_fuse_symlink(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_mkdir:"
" linkname=%s;"
" name=%s;"
" linkname={};"
" name={};"
"\n"
,
linkname,
@@ -514,9 +516,9 @@ debug_fuse_rename_in(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_rename_in:"
" oldname=%s;"
" oldname={};"
" newdir={};"
" newname=%s;"
" newname={};"
"\n"
,
oldname,
@@ -536,7 +538,7 @@ debug_fuse_link_in(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_link_in:"
" oldnodeid={};"
" name=%s;"
" name={};"
"\n"
,
arg->oldnodeid,
@@ -556,7 +558,7 @@ debug_fuse_create_in(const void *arg_)
"fuse_create_in:"
" mode={:o};"
" umask={:o};"
" name=%s;"
" name={};"
" flags=0x%X (",
arg->mode,
arg->umask,
@@ -574,7 +576,7 @@ debug_fuse_open_in(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_open_in:"
" flags=0x%08X (",
" flags=0x{:08X} (",
arg->flags);
debug_open_flags(arg->flags);
fmt::print(g_OUTPUT,");\n");
@@ -639,7 +641,7 @@ debug_fuse_write_in(const void *arg_)
if(str == NULL)
continue;
fmt::print(g_OUTPUT,"%s,",str);
fmt::print(g_OUTPUT,"{},",str);
}
fmt::print(g_OUTPUT,");\n");
}
@@ -712,8 +714,8 @@ debug_fuse_setxattr_in(const void *arg_)
"fuse_setxattr_in:"
" size={};"
" flags=0x%X;"
" name=%s;"
" value=%s;"
" name={};"
" value={};"
"\n"
,
arg->size,
@@ -734,7 +736,7 @@ debug_fuse_getxattr_in(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_getxattr_in:"
" size={};"
" name=%s;"
" name={};"
"\n"
,
arg->size,
@@ -763,7 +765,7 @@ debug_fuse_removexattr(const void *arg_)
fmt::print(g_OUTPUT,
"fuse_removexattr:"
" name=%s;"
" name={};"
"\n"
,
name);
@@ -816,7 +818,7 @@ debug_fuse_init_in(const struct fuse_init_in *arg_)
if(str == NULL)
continue;
fmt::print(g_OUTPUT,"%s, ",str);
fmt::print(g_OUTPUT,"{}, ",str);
}
fmt::print(g_OUTPUT,")\n");
}
@@ -932,7 +934,7 @@ debug_fuse_init_out(const uint64_t unique_,
if(str == NULL)
continue;
fmt::print(g_OUTPUT,"%s, ",str);
fmt::print(g_OUTPUT,"{}, ",str);
}
fmt::print(g_OUTPUT,
@@ -1100,13 +1102,14 @@ opcode_name(enum fuse_opcode op_)
{
static const char *names[] =
{
"INVALID",
"INVALID0",
"LOOKUP",
"FORGET",
"GETATTR",
"SETATTR",
"READLINK",
"SYMLINK",
"INVALID1",
"MKNOD",
"MKDIR",
"UNLINK",
@@ -1118,6 +1121,7 @@ opcode_name(enum fuse_opcode op_)
"WRITE",
"STATFS",
"RELEASE",
"INVALID3",
"FSYNC",
"SETXATTR",
"GETXATTR",
@@ -1147,7 +1151,10 @@ opcode_name(enum fuse_opcode op_)
"LSEEK",
"COPY_FILE_RANGE",
"SETUPMAPPING",
"REMOVEMAPPING"
"REMOVEMAPPING",
"SYNCFS",
"TMPFILE",
"STATX"
};
if(op_ >= (sizeof(names) / sizeof(names[0])))
@@ -1161,9 +1168,9 @@ debug_fuse_in_header(const struct fuse_in_header *hdr_)
{
const void *arg = &hdr_[1];
fmt::print(stderr,
"unique=0x%016" PRIx64 ";"
" opcode=%s ({});"
fmt::print(g_OUTPUT,
"unique=0x{:016x};"
" opcode={} ({});"
" nodeid={};"
" uid={};"
" gid={};"
@@ -1265,8 +1272,14 @@ debug_fuse_in_header(const struct fuse_in_header *hdr_)
case FUSE_INTERRUPT:
debug_fuse_interrupt_in(arg);
break;
case FUSE_SYNCFS:
break;
case FUSE_TMPFILE:
break;
case FUSE_STATX:
break;
default:
fmt::print(g_OUTPUT,"FIXME\n");
fmt::print(g_OUTPUT,"FIXME: {}\n",hdr_->opcode);
break;
}
}
@@ -1275,10 +1288,10 @@ void
debug_fuse_out_header(const struct fuse_out_header *hdr_)
{
fmt::print(g_OUTPUT,
"unique=0x%016" PRIx64 ";"
"unique=0x{:016x};"
" opcode=RESPONSE;"
" error=%d (%s);"
" len={};"
" error={} ({});"
" len={};\n"
,
hdr_->unique,
hdr_->error,
@@ -1312,7 +1325,7 @@ debug_fuse_readlink(const uint64_t unique_,
" opcode=RESPONSE;"
" error=0 (Success);"
" len={}; || "
"readlink: linkname=%s"
"readlink: linkname={}"
"\n"
,
unique_,
+1
View File
@@ -51,6 +51,7 @@ void debug_fuse_lk_out(const uint64_t unique,
void debug_fuse_bmap_out(const uint64_t unique,
const struct fuse_bmap_out *arg);
void debug_fuse_in_header(const struct fuse_in_header *hdr);
void debug_fuse_out_header(const struct fuse_out_header *hdr);
std::string fuse_debug_init_flag_name(const uint64_t);
+33 -75
View File
@@ -152,7 +152,7 @@ struct fuse_dh
fuse_dirents_t d;
};
static struct fuse f = {0};
static struct fuse f = {};
/*
Why was the nodeid:generation logic simplified?
@@ -177,48 +177,6 @@ fuse_hdr_arg(const struct fuse_in_header *hdr_)
return (void*)&hdr_[1];
}
static
void
list_add(struct list_head *new_,
struct list_head *prev_,
struct list_head *next_)
{
next_->prev = new_;
new_->next = next_;
new_->prev = prev_;
prev_->next = new_;
}
static
inline
void
list_add_head(struct list_head *new_,
struct list_head *head_)
{
list_add(new_,head_,head_->next);
}
static
inline
void
list_add_tail(struct list_head *new_,
struct list_head *head_)
{
list_add(new_,head_->prev,head_);
}
static
inline
void
list_del(struct list_head *entry)
{
struct list_head *prev = entry->prev;
struct list_head *next = entry->next;
next->prev = prev;
prev->next = next;
}
static
size_t
id_hash(uint64_t ino)
@@ -1020,7 +978,7 @@ get_path_common(uint64_t nodeid,
err = try_get_path(nodeid,name,path,wnode,true);
if(err == -EAGAIN)
{
struct lock_queue_element qe = {0};
struct lock_queue_element qe = {};
qe.nodeid1 = nodeid;
qe.name1 = name;
@@ -1079,7 +1037,7 @@ get_path2(uint64_t nodeid1,
path1,path2,wnode1,wnode2);
if(err == -EAGAIN)
{
struct lock_queue_element qe = {0};
struct lock_queue_element qe = {};
qe.nodeid1 = nodeid1;
qe.name1 = name1;
@@ -1157,7 +1115,7 @@ forget_node(const uint64_t nodeid,
*/
while(node->nlookup == nlookup && node->treelock)
{
struct lock_queue_element qe = {0};
struct lock_queue_element qe = {};
qe.nodeid1 = nodeid;
@@ -1389,7 +1347,7 @@ fuse_lib_lookup(fuse_req_t *req_,
char *fusepath;
const char *name;
node_t *dot = NULL;
struct fuse_entry_param e = {0};
struct fuse_entry_param e = {};
name = (const char*)fuse_hdr_arg(hdr_);
nodeid = hdr_->nodeid;
@@ -1622,7 +1580,7 @@ fuse_lib_setattr(fuse_req_t *req_,
struct fuse_in_header *hdr_)
{
uint64_t fh;
struct stat stbuf = {0};
struct stat stbuf = {};
char *fusepath;
int err;
fuse_timeouts_t timeout;
@@ -1678,12 +1636,12 @@ fuse_lib_setattr(fuse_req_t *req_,
if(arg->valid & FATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if(arg->valid & FATTR_ATIME)
tv[0] = (struct timespec){ static_cast<time_t>(arg->atime), arg->atimensec };
tv[0] = (struct timespec){ static_cast<time_t>(arg->atime), static_cast<long>(arg->atimensec) };
if(arg->valid & FATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if(arg->valid & FATTR_MTIME)
tv[1] = (struct timespec){ static_cast<time_t>(arg->mtime), arg->mtimensec };
tv[1] = (struct timespec){ static_cast<time_t>(arg->mtime), static_cast<long>(arg->mtimensec) };
err = ((fusepath != NULL) ?
f.ops.utimens(&req_->ctx,&fusepath[1],tv) :
@@ -1927,7 +1885,7 @@ fuse_lib_symlink(fuse_req_t *req_,
char *fusepath;
const char *name;
const char *linkname;
struct fuse_entry_param e = {0};
struct fuse_entry_param e = {};
name = (const char*)fuse_hdr_arg(hdr_);
linkname = (name + strlen(name) + 1);
@@ -2017,7 +1975,7 @@ fuse_lib_link(fuse_req_t *req_,
char *newpath;
const char *newname;
struct fuse_link_in *arg;
struct fuse_entry_param e = {0};
struct fuse_entry_param e = {};
arg = (fuse_link_in*)fuse_hdr_arg(hdr_);
newname = (const char*)PARAM(arg);
@@ -2074,7 +2032,7 @@ fuse_lib_create(fuse_req_t *req_,
char *fusepath;
const char *name;
uint64_t new_nodeid;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_entry_param e;
struct fuse_create_in *arg;
@@ -2188,7 +2146,7 @@ fuse_lib_open(fuse_req_t *req_,
{
int err;
char *fusepath;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_open_in *arg;
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
@@ -2231,7 +2189,7 @@ fuse_lib_read(fuse_req_t *req_,
struct fuse_in_header *hdr_)
{
int res;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_read_in *arg;
fuse_msgbuf_t *msgbuf;
@@ -2266,7 +2224,7 @@ fuse_lib_write(fuse_req_t *req_,
{
int res;
char *data;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_write_in *arg;
arg = (fuse_write_in*)fuse_hdr_arg(hdr_);
@@ -2335,8 +2293,8 @@ fuse_lib_opendir(fuse_req_t *req_,
int err;
char *fusepath;
struct fuse_dh *dh;
fuse_file_info_t llffi = {0};
fuse_file_info_t ffi = {0};
fuse_file_info_t llffi = {};
fuse_file_info_t ffi = {};
struct fuse_open_in *arg;
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
@@ -2422,8 +2380,8 @@ fuse_lib_readdir(fuse_req_t *req_,
size_t size;
fuse_dirents_t *d;
struct fuse_dh *dh;
fuse_file_info_t ffi = {0};
fuse_file_info_t llffi = {0};
fuse_file_info_t ffi = {};
fuse_file_info_t llffi = {};
struct fuse_read_in *arg;
arg = (fuse_read_in*)fuse_hdr_arg(hdr_);
@@ -2466,8 +2424,8 @@ fuse_lib_readdir_plus(fuse_req_t *req_,
size_t size;
fuse_dirents_t *d;
struct fuse_dh *dh;
fuse_file_info_t ffi = {0};
fuse_file_info_t llffi = {0};
fuse_file_info_t ffi = {};
fuse_file_info_t llffi = {};
struct fuse_read_in *arg;
arg = (fuse_read_in*)fuse_hdr_arg(hdr_);
@@ -2508,7 +2466,7 @@ fuse_lib_releasedir(fuse_req_t *req_,
{
struct fuse_dh *dh;
fuse_file_info_t ffi;
fuse_file_info_t llffi = {0};
fuse_file_info_t llffi = {};
struct fuse_release_in *arg;
arg = (fuse_release_in*)fuse_hdr_arg(hdr_);
@@ -2537,7 +2495,7 @@ fuse_lib_fsyncdir(fuse_req_t *req_,
int err;
int is_datasync;
fuse_file_info_t ffi;
fuse_file_info_t llffi = {0};
fuse_file_info_t llffi = {};
struct fuse_fsync_in *arg;
arg = (fuse_fsync_in*)fuse_hdr_arg(hdr_);
@@ -2560,7 +2518,7 @@ fuse_lib_statfs(fuse_req_t *req_,
{
int err = 0;
char *fusepath;
struct statvfs buf = {0};
struct statvfs buf = {};
fusepath = NULL;
if(hdr_->nodeid)
@@ -2767,8 +2725,8 @@ fuse_lib_copy_file_range(fuse_req_t *req_,
const struct fuse_in_header *hdr_)
{
ssize_t rv;
fuse_file_info_t ffi_in = {0};
fuse_file_info_t ffi_out = {0};
fuse_file_info_t ffi_in = {};
fuse_file_info_t ffi_out = {};
const struct fuse_copy_file_range_in *arg;
arg = (fuse_copy_file_range_in*)fuse_hdr_arg(hdr_);
@@ -2825,7 +2783,7 @@ fuse_lib_tmpfile(fuse_req_t *req_,
int err;
char *fusepath;
const char *name;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_entry_param e;
struct fuse_create_in *arg;
@@ -3072,7 +3030,7 @@ fuse_lib_release(fuse_req_t *req_,
struct fuse_in_header *hdr_)
{
int err = 0;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_release_in *arg;
arg = (fuse_release_in*)fuse_hdr_arg(hdr_);
@@ -3109,7 +3067,7 @@ fuse_lib_flush(fuse_req_t *req_,
struct fuse_in_header *hdr_)
{
int err;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
struct fuse_flush_in *arg;
arg = (fuse_flush_in*)fuse_hdr_arg(hdr_);
@@ -3167,7 +3125,7 @@ fuse_lib_getlk(fuse_req_t *req_,
lock_t lk;
struct flock flk;
lock_t *conflict;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
const struct fuse_lk_in *arg;
arg = (fuse_lk_in*)fuse_hdr_arg(hdr_);
@@ -3270,7 +3228,7 @@ fuse_lib_ioctl(fuse_req_t *req_,
int err;
char *out_buf = NULL;
fuse_file_info_t ffi;
fuse_file_info_t llffi = {0};
fuse_file_info_t llffi = {};
const void *in_buf;
uint32_t out_size;
const struct fuse_ioctl_in *arg;
@@ -3339,7 +3297,7 @@ fuse_lib_poll(fuse_req_t *req_,
{
int err;
unsigned revents = 0;
fuse_file_info_t ffi = {0};
fuse_file_info_t ffi = {};
fuse_pollhandle_t *ph = NULL;
const struct fuse_poll_in *arg;
@@ -3428,7 +3386,7 @@ fuse_prune_some_remembered_nodes(int *offset_)
pruned = 0;
checked = 0;
now = current_time();
while(*offset_ < kv_size(f.remembered_nodes))
while(*offset_ < (int)kv_size(f.remembered_nodes))
{
time_t age;
remembered_node_t *fn = &kv_A(f.remembered_nodes,*offset_);
@@ -3913,7 +3871,7 @@ fuse_passthrough_open(const int fd_)
{
int rv;
int dev_fuse_fd;
struct fuse_backing_map bm = {0};
struct fuse_backing_map bm = {};
dev_fuse_fd = fuse_chan_fd(f.se->ch);
bm.fd = fd_;
+27 -136
View File
@@ -292,7 +292,7 @@ int
fuse_reply_entry(fuse_req_t *req,
const struct fuse_entry_param *e)
{
struct fuse_entry_out arg = {0};
struct fuse_entry_out arg = {};
size_t size = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg);
@@ -322,7 +322,7 @@ fuse_reply_create(fuse_req_t *req,
const struct fuse_entry_param *e,
const fuse_file_info_t *f)
{
struct fuse_create_out buf = {0};
struct fuse_create_out buf = {};
size_t entrysize = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out);
struct fuse_entry_out *earg = (struct fuse_entry_out*)&buf.e;
@@ -339,7 +339,7 @@ fuse_reply_attr(fuse_req_t *req,
const struct stat *attr,
const uint64_t timeout)
{
struct fuse_attr_out arg = {0};
struct fuse_attr_out arg = {};
size_t size = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
@@ -377,7 +377,7 @@ int
fuse_reply_open(fuse_req_t *req,
const fuse_file_info_t *f)
{
struct fuse_open_out arg = {0};
struct fuse_open_out arg = {};
fill_open(&arg, f);
@@ -388,7 +388,7 @@ int
fuse_reply_write(fuse_req_t *req,
size_t count)
{
struct fuse_write_out arg = {0};
struct fuse_write_out arg = {};
arg.size = count;
@@ -403,55 +403,6 @@ fuse_reply_buf(fuse_req_t *req,
return send_reply_ok(req, buf, size);
}
static
int
fuse_send_data_iov_fallback(struct fuse_ll *f,
struct fuse_chan *ch,
struct iovec *iov,
int iov_count,
struct fuse_bufvec *buf,
size_t len)
{
int res;
struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len);
/* Optimize common case */
if(buf->count == 1 && buf->idx == 0 && buf->off == 0 &&
!(buf->buf[0].flags & FUSE_BUF_IS_FD))
{
/* FIXME: also avoid memory copy if there are multiple buffers
but none of them contain an fd */
iov[iov_count].iov_base = buf->buf[0].mem;
iov[iov_count].iov_len = len;
iov_count++;
return fuse_send_msg(f, ch, iov, iov_count);
}
fuse_msgbuf_t *msgbuf;
msgbuf = msgbuf_alloc();
if(msgbuf == NULL)
return -ENOMEM;
mem_buf.buf[0].mem = msgbuf->mem;
res = fuse_buf_copy(&mem_buf, buf, (fuse_buf_copy_flags)0);
if(res < 0)
{
msgbuf_free(msgbuf);
return -res;
}
len = res;
iov[iov_count].iov_base = msgbuf->mem;
iov[iov_count].iov_len = len;
iov_count++;
res = fuse_send_msg(f, ch, iov, iov_count);
msgbuf_free(msgbuf);
return res;
}
struct fuse_ll_pipe
{
size_t size;
@@ -468,21 +419,6 @@ fuse_ll_pipe_free(struct fuse_ll_pipe *llp)
free(llp);
}
static
int
fuse_send_data_iov(struct fuse_ll *f,
struct fuse_chan *ch,
struct iovec *iov,
int iov_count,
struct fuse_bufvec *buf,
unsigned int flags)
{
size_t len = fuse_buf_size(buf);
(void) flags;
return fuse_send_data_iov_fallback(f, ch, iov, iov_count, buf, len);
}
int
fuse_reply_data(fuse_req_t *req,
char *buf_,
@@ -516,7 +452,7 @@ int
fuse_reply_statfs(fuse_req_t *req,
const struct statvfs *stbuf)
{
struct fuse_statfs_out arg = {0};
struct fuse_statfs_out arg = {};
size_t size = req->f->conn.proto_minor < 4 ?
FUSE_COMPAT_STATFS_SIZE : sizeof(arg);
@@ -529,7 +465,7 @@ int
fuse_reply_xattr(fuse_req_t *req,
size_t count)
{
struct fuse_getxattr_out arg = {0};
struct fuse_getxattr_out arg = {};
arg.size = count;
@@ -540,7 +476,7 @@ int
fuse_reply_lock(fuse_req_t *req,
const struct flock *lock)
{
struct fuse_lk_out arg = {0};
struct fuse_lk_out arg = {};
arg.lk.type = lock->l_type;
if(lock->l_type != F_UNLCK)
@@ -560,7 +496,7 @@ int
fuse_reply_bmap(fuse_req_t *req,
uint64_t idx)
{
struct fuse_bmap_out arg = {0};
struct fuse_bmap_out arg = {};
arg.block = idx;
@@ -595,7 +531,7 @@ fuse_reply_ioctl_retry(fuse_req_t *req,
const struct iovec *out_iov,
size_t out_count)
{
struct fuse_ioctl_out arg = {0};
struct fuse_ioctl_out arg = {};
struct fuse_ioctl_iovec *in_fiov = NULL;
struct fuse_ioctl_iovec *out_fiov = NULL;
struct iovec iov[4];
@@ -705,7 +641,7 @@ fuse_reply_ioctl_iov(fuse_req_t *req,
int count)
{
struct iovec *padded_iov;
struct fuse_ioctl_out arg = {0};
struct fuse_ioctl_out arg = {};
int res;
padded_iov = (iovec*)malloc((count + 2) * sizeof(struct iovec));
@@ -728,7 +664,7 @@ int
fuse_reply_poll(fuse_req_t *req,
unsigned revents)
{
struct fuse_poll_out arg = {0};
struct fuse_poll_out arg = {};
arg.revents = revents;
@@ -1015,7 +951,7 @@ do_setlk_common(fuse_req_t *req,
int sleep)
{
struct flock flock;
fuse_file_info_t fi = {0};
fuse_file_info_t fi = {};
struct fuse_lk_in *arg = (struct fuse_lk_in*)inarg;
fi.fh = arg->fh;
@@ -1118,7 +1054,7 @@ void
do_init(fuse_req_t *req,
struct fuse_in_header *hdr_)
{
struct fuse_init_out outarg = {0};
struct fuse_init_out outarg = {};
struct fuse_init_in *arg = (struct fuse_init_in *)&hdr_[1];
struct fuse_ll *f = req->f;
size_t bufsize;
@@ -1604,50 +1540,6 @@ fuse_lowlevel_notify_delete(struct fuse_chan *ch,
return send_notify_iov(f, ch, FUSE_NOTIFY_DELETE, iov, 3);
}
int
fuse_lowlevel_notify_store(struct fuse_chan *ch,
uint64_t ino,
off_t offset,
struct fuse_bufvec *bufv,
enum fuse_buf_copy_flags flags)
{
struct fuse_out_header out;
struct fuse_notify_store_out outarg;
struct fuse_ll *f;
struct iovec iov[3];
size_t size = fuse_buf_size(bufv);
int res;
if(!ch)
return -EINVAL;
f = (struct fuse_ll*)fuse_session_data(fuse_chan_session(ch));
if(!f)
return -ENODEV;
if(f->conn.proto_minor < 15)
return -ENOSYS;
out.unique = 0;
out.error = FUSE_NOTIFY_STORE;
outarg.nodeid = ino;
outarg.offset = offset;
outarg.size = size;
outarg.padding = 0;
iov[0].iov_base = &out;
iov[0].iov_len = sizeof(out);
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
res = fuse_send_data_iov(f, ch, iov, 2, bufv, flags);
if(res > 0)
res = -res;
return res;
}
struct fuse_retrieve_req
{
struct fuse_notify_req nreq;
@@ -1825,8 +1717,8 @@ fuse_send_errno(struct fuse_ll *f_,
const int errno_,
const uint64_t unique_id_)
{
struct fuse_out_header out = {0};
struct iovec iov = {0};
struct fuse_out_header out = {};
struct iovec iov = {};
out.unique = unique_id_;
out.error = -errno_;
@@ -1845,14 +1737,14 @@ fuse_send_enomem(struct fuse_ll *f_,
fuse_send_errno(f_,ch_,ENOMEM,unique_id_);
}
static
void
fuse_send_einval(struct fuse_ll *f_,
struct fuse_chan *ch_,
const uint64_t unique_id_)
{
fuse_send_errno(f_,ch_,EINVAL,unique_id_);
}
// static
// void
// fuse_send_einval(struct fuse_ll *f_,
// struct fuse_chan *ch_,
// const uint64_t unique_id_)
// {
// fuse_send_errno(f_,ch_,EINVAL,unique_id_);
// }
static
int
@@ -1885,9 +1777,6 @@ fuse_ll_buf_process_read(struct fuse_session *se_,
in = (struct fuse_in_header*)msgbuf_->mem;
if((in->uid == FUSE_INVALID_UIDGID) || (in->gid == FUSE_INVALID_UIDGID))
return fuse_send_einval(se_->f,se_->ch,in->unique);
req = fuse_ll_alloc_req(se_->f);
if(req == NULL)
return fuse_send_enomem(se_->f,se_->ch,in->unique);
@@ -1908,6 +1797,8 @@ fuse_ll_buf_process_read(struct fuse_session *se_,
if(fuse_ll_funcs[in->opcode] == NULL)
goto reply_err;
// debug_fuse_in_header(in);
fuse_ll_funcs[in->opcode](req, in);
return;
@@ -1971,7 +1862,7 @@ fuse_ll_opt_proc(void *data_,
int key_,
struct fuse_args *outargs_)
{
fmt::print(stderr, "* ERROR: unknown option '{}'\n", arg_);
fmt::print(stderr, "fuse: ERROR - unknown option - '{}'\n", arg_);
return -1;
}
+1 -1
View File
@@ -282,7 +282,7 @@ fuse_setup_common(int argc,
if (res == -1)
goto err_unmount;
res = fuse_set_signal_handlers(fuse_get_session(fuse));
res = fuse_set_signal_handlers(fuse_get_session());
if (res == -1)
goto err_unmount;
+10 -6
View File
@@ -184,13 +184,16 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
}
/* Check if kernel is doing init in background */
static int init_backgrounded(void)
static
int
init_backgrounded(void)
{
unsigned ibg, len;
unsigned ibg;
size_t len;
len = sizeof(ibg);
if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
if(sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
return 0;
return ibg;
@@ -298,10 +301,12 @@ static int fuse_mount_core(const char *mountpoint, const char *opts)
return fd;
}
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
int
fuse_kern_mount(const char *mountpoint,
struct fuse_args *args)
{
struct mount_opts mo;
int res = -1;
struct mount_opts mo;
memset(&mo, 0, sizeof(mo));
/* mount util should not try to spawn the daemon */
@@ -317,7 +322,6 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
return 0;
res = fuse_mount_core(mountpoint, mo.kernel_opts);
out:
free(mo.kernel_opts);
return res;
}
+1 -1
View File
@@ -17,7 +17,7 @@
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/poll.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
+922 -919
View File
File diff suppressed because it is too large Load Diff
+182 -177
View File
@@ -16,216 +16,221 @@ static char *progname;
static char *xstrdup(const char *s)
{
char *t = strdup(s);
if (!t) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
exit(1);
}
return t;
char *t = strdup(s);
if (!t) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
exit(1);
}
return t;
}
static void *xrealloc(void *oldptr, size_t size)
{
void *ptr = realloc(oldptr, size);
if (!ptr) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
exit(1);
}
return ptr;
void *ptr = realloc(oldptr, size);
if (!ptr) {
fprintf(stderr, "%s: failed to allocate memory\n", progname);
exit(1);
}
return ptr;
}
static void add_arg(char **cmdp, const char *opt)
{
size_t optlen = strlen(opt);
size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
char *s;
s = cmd + cmdlen;
if (*cmdp)
*s++ = ' ';
size_t optlen = strlen(opt);
size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
char *s;
s = cmd + cmdlen;
if (*cmdp)
*s++ = ' ';
*s++ = '\'';
for (; *opt; opt++) {
if (*opt == '\'') {
*s++ = '\'';
*s++ = '\\';
*s++ = '\'';
*s++ = '\'';
} else
*s++ = *opt;
}
*s++ = '\'';
*s = '\0';
*cmdp = cmd;
*s++ = '\'';
for (; *opt; opt++) {
if (*opt == '\'') {
*s++ = '\'';
*s++ = '\\';
*s++ = '\'';
*s++ = '\'';
} else
*s++ = *opt;
}
*s++ = '\'';
*s = '\0';
*cmdp = cmd;
}
static char *add_option(const char *opt, char *options)
{
int oldlen = options ? strlen(options) : 0;
int oldlen = options ? strlen(options) : 0;
options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
if (!oldlen)
strcpy(options, opt);
else {
strcat(options, ",");
strcat(options, opt);
}
return options;
options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
if (!oldlen)
strcpy(options, opt);
else {
strcat(options, ",");
strcat(options, opt);
}
return options;
}
int main(int argc, char *argv[])
{
char *type = NULL;
char *source;
const char *mountpoint;
char *basename;
char *options = NULL;
char *command = NULL;
char *setuid = NULL;
int i;
int dev = 1;
int suid = 1;
char *type = NULL;
char *source;
const char *mountpoint;
char *basename;
char *options = NULL;
char *command = NULL;
char *setuid = NULL;
int i;
int dev = 1;
int suid = 1;
progname = argv[0];
basename = strrchr(argv[0], '/');
if (basename)
basename++;
else
basename = argv[0];
progname = argv[0];
basename = strrchr(argv[0], '/');
if (basename)
basename++;
else
basename = argv[0];
type = "mergerfs";
if (strncmp(basename, "mount.fuse.", 11) == 0)
type = basename + 11;
if (strncmp(basename, "mount.fuseblk.", 14) == 0)
type = basename + 14;
type = "mergerfs";
if (strncmp(basename, "mount.fuse.", 11) == 0)
type = basename + 11;
if (strncmp(basename, "mount.fuseblk.", 14) == 0)
type = basename + 14;
if (type && !type[0])
type = NULL;
if (type && !type[0])
type = NULL;
if (argc < 3) {
fprintf(stderr,
"usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
progname, type ? "source" : "type#[source]");
exit(1);
}
if (argc < 3) {
fprintf(stderr,
"usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
progname, type ? "source" : "type#[source]");
exit(1);
}
source = argv[1];
if (!source[0])
source = NULL;
source = argv[1];
if (!source[0])
source = NULL;
mountpoint = argv[2];
mountpoint = argv[2];
for (i = 3; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
continue;
} else if (strcmp(argv[i], "-t") == 0) {
i++;
for (i = 3; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
continue;
} else if (strcmp(argv[i], "-t") == 0) {
i++;
if (i == argc) {
fprintf(stderr,
"%s: missing argument to option '-t'\n",
progname);
exit(1);
}
type = argv[i];
if (strncmp(type, "fuse.", 5) == 0)
type += 5;
else if (strncmp(type, "fuseblk.", 8) == 0)
type += 8;
if (i == argc) {
fprintf(stderr,
"%s: missing argument to option '-t'\n",
progname);
exit(1);
}
type = argv[i];
if (strncmp(type, "fuse.", 5) == 0)
type += 5;
else if (strncmp(type, "fuseblk.", 8) == 0)
type += 8;
if (!type[0]) {
fprintf(stderr,
"%s: empty type given as argument to option '-t'\n",
progname);
exit(1);
}
} else if (strcmp(argv[i], "-o") == 0) {
char *opts;
char *opt;
i++;
if (i == argc)
break;
if (!type[0]) {
fprintf(stderr,
"%s: empty type given as argument to option '-t'\n",
progname);
exit(1);
}
} else if (strcmp(argv[i], "-o") == 0) {
char *opts;
char *opt;
i++;
if (i == argc)
break;
opts = xstrdup(argv[i]);
opt = strtok(opts, ",");
while (opt) {
int j;
int ignore = 0;
const char *ignore_opts[] = { "",
"user",
"nouser",
"users",
"auto",
"noauto",
"_netdev",
NULL};
if (strncmp(opt, "setuid=", 7) == 0) {
setuid = xstrdup(opt + 7);
ignore = 1;
}
for (j = 0; ignore_opts[j]; j++)
if (strcmp(opt, ignore_opts[j]) == 0)
ignore = 1;
opts = xstrdup(argv[i]);
opt = strtok(opts, ",");
while (opt)
{
int j;
int ignore = 0;
const char *ignore_opts[] =
{
"",
"nofail",
"user",
"nouser",
"users",
"auto",
"noauto",
"_netdev",
NULL
};
if (strncmp(opt, "setuid=", 7) == 0) {
setuid = xstrdup(opt + 7);
ignore = 1;
}
for (j = 0; ignore_opts[j]; j++)
if (strcmp(opt, ignore_opts[j]) == 0)
ignore = 1;
if (!ignore) {
if (strcmp(opt, "nodev") == 0)
dev = 0;
else if (strcmp(opt, "nosuid") == 0)
suid = 0;
if (!ignore) {
if (strcmp(opt, "nodev") == 0)
dev = 0;
else if (strcmp(opt, "nosuid") == 0)
suid = 0;
options = add_option(opt, options);
}
opt = strtok(NULL, ",");
}
}
}
options = add_option(opt, options);
}
opt = strtok(NULL, ",");
}
}
}
if (dev)
options = add_option("dev", options);
if (suid)
options = add_option("suid", options);
if (dev)
options = add_option("dev", options);
if (suid)
options = add_option("suid", options);
if (!type) {
if (source) {
type = xstrdup(source);
source = strchr(type, '#');
if (source)
*source++ = '\0';
if (!type[0]) {
fprintf(stderr, "%s: empty filesystem type\n",
progname);
exit(1);
}
} else {
fprintf(stderr, "%s: empty source\n", progname);
exit(1);
}
}
if (!type) {
if (source) {
type = xstrdup(source);
source = strchr(type, '#');
if (source)
*source++ = '\0';
if (!type[0]) {
fprintf(stderr, "%s: empty filesystem type\n",
progname);
exit(1);
}
} else {
fprintf(stderr, "%s: empty source\n", progname);
exit(1);
}
}
add_arg(&command, type);
if (source)
add_arg(&command, source);
add_arg(&command, mountpoint);
if (options) {
add_arg(&command, "-o");
add_arg(&command, options);
}
add_arg(&command, type);
if (source)
add_arg(&command, source);
add_arg(&command, mountpoint);
if (options) {
add_arg(&command, "-o");
add_arg(&command, options);
}
if (setuid && setuid[0]) {
char *sucommand = command;
command = NULL;
add_arg(&command, "su");
add_arg(&command, "-");
add_arg(&command, setuid);
add_arg(&command, "-c");
add_arg(&command, sucommand);
} else if (!getenv("HOME")) {
/* Hack to make filesystems work in the boot environment */
setenv("HOME", "/root", 0);
}
if (setuid && setuid[0]) {
char *sucommand = command;
command = NULL;
add_arg(&command, "su");
add_arg(&command, "-");
add_arg(&command, setuid);
add_arg(&command, "-c");
add_arg(&command, sucommand);
} else if (!getenv("HOME")) {
/* Hack to make filesystems work in the boot environment */
setenv("HOME", "/root", 0);
}
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
strerror(errno));
return 1;
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
fprintf(stderr, "%s: failed to execute /bin/sh: %s\n", progname,
strerror(errno));
return 1;
}
+6 -6
View File
@@ -12,16 +12,16 @@ is a FUSE-based union filesystem geared towards simplifying storage and manageme
and
.BR aufs .
.SH QUICKSTART
.UR https://trapexit.github.io/mergerfs/quickstart
.LI https://trapexit.github.io/mergerfs/quickstart
.UR https://trapexit.github.io/mergerfs/latest/quickstart
.LI https://trapexit.github.io/mergerfs/latest/quickstart
.UE
.SH DOCUMENTATION
.UR https://trapexit.github.io/mergerfs
.LI https://trapexit.github.io/mergerfs
.UE
.SH SUPPORT
.UR https://trapexit.github.io/mergerfs/support
.LI https://trapexit.github.io/mergerfs/support
.UR https://trapexit.github.io/mergerfs/latest/support
.LI https://trapexit.github.io/mergerfs/latest/support
.UE
.SH FEATURES
.IP \[bu] 2
@@ -74,8 +74,8 @@ When a file or directory is created, a policy is first run to determine which br
For functions which change attributes or remove the file, the behavior may be applied to all instances found.
Read more about policies here:
.UR https://trapexit.github.io/mergerfs/config/functions_categories_and_policies
.LI https://trapexit.github.io/mergerfs/config/functions_categories_and_policies
.UR https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies
.LI https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies
.UE
.SH VISUALIZATION
.nf
@@ -116,6 +116,7 @@ but it makes things a bit more uniform.
| mfs (most free space) | Pick the branch with the most available free space. |
| ff (first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found. |
| lfs (least free space) | Pick the branch with the least available free space. |
| lup (least used percent) | Pick the branch with the least used percentage of space |
| lus (least used space) | Pick the branch with the least used space. |
| all | Search: For **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
| msppfrd (most shared path, percentage free random distribution) | Like **eppfrd** but if it fails to find a branch it will try again with the parent directory. Continues this pattern till finding one. |
@@ -0,0 +1,27 @@
# lazy-umount-mountpoint
* type: `BOOL`
* default: `false`
* example: `lazy-umount-mountpoint=true`
When enabled mergerfs will issue a [lazy
umount](https://man7.org/linux/man-pages/man8/umount.8.html) request
to the mount point as it starts.
```
Lazy unmount. Detach the filesystem from the file hierarchy
now, and clean up all references to this filesystem as soon as
it is not busy anymore.
```
The value of using this is in situations where you wish to change
options which can only be set at initialization or upgrade mergerfs
itself. It also helps in the case of using `mount -a` which leads to a
new instance of mergerfs being mounted due to it being unable to
recognize that it is already mounted.
In theory mergerfs could notice that an instance is already mounted at
the mount point and attempt to use the runtime interface to modify the
options and only mounting a new instance if the version has changed or
initialization time only options are changed. However, given the
relative rarity of this being an issue it isn't a priority.
+3 -6
View File
@@ -213,17 +213,14 @@ config file.
* **passthrough.max-stack-depth=INT**: Set to `1` another filesystem
can be stacked on mergerfs. Set to `2` to have mergerfs stacked over
another filesystem. (default: 1)
* **gid-cache.expire-timeout=INT**: Number of seconds till
supplemental group data is refreshed in the [GID
cache](../known_issues_bugs.md#supplemental-user-groups). (default:
3600)
* **gid-cache.remove-timeout=INT**: Number of seconds to wait till
cached data is removed due to lack of usage. (default: 43200)
* **remember-nodes=INT**: The number of seconds to keep the internal
representation of a file once the OS tells mergerfs it is no longer
needed. Really only needed for [exporting mergerfs via
NFS](../remote_filesystems.md) (default: 0)
* **noforget**: Effectively sets `remember-nodes` to infinity.
* **allow-idmap=BOOL**: Enables idmap (identity mapping). (default:
true)
**NOTE:** Options are evaluated in the order listed so if the options
are **func.rmdir=rand,category.action=ff** the **action** category
@@ -5,16 +5,10 @@
[Primarily Linux.](../setup/installation.md) FreeBSD is casually
supported but not well tested.
With FreeBSD certain Linux functions and FUSE features are not
supported. In many cases the absense will not be noticed however
performance may be impacted due to a core mergerfs design decision
relying on a Linux feature.
Linux allows individual threads to change credentials whereas [FreeBSD
does not](https://wiki.freebsd.org/Per-Thread%20Credentials). As a
result mergerfs must use a lock to ensure critical sections which need
to change credentials are safeguarded. This will limit throughput on
systems where requests to mergerfs come from multiple user identities.
With FreeBSD certain Linux functions and FUSE features [are not
supported.](../known_issues_bugs.md#freebsd-version) In many cases the
absence will not be noticed however more advanced features will not be
available.
### Why not support MacOS?
@@ -36,29 +30,47 @@ level API.
Windows, while used for NAS systems more often than MacOS, is still
relatively uncommon when compared to Linux. [Drive
Pool](../project_comparisons.md#stablebits-drivepool) is a reasonable
alternative.
Pool](../project_comparisons.md#stablebits-drivepool) is a good
alternative. It may also be possible to use mergerfs with WSL.
## What filesystems can be used as branches?
ext4, btrfs, xfs, f2fs, zfs, nfs, etc.
On the surface any filesystem should work but there could be issues
with non-POSIX compliant filesystems such as vfat, ntfs, cifs, exfat,
etc. When directories need to be created or files moved by mergerfs if
the filesystem returns errors due to not supporting certain POSIX
filesystem features it could result in the core functions failing.
Most any filesystem should work but there could be issues with
non-POSIX compliant filesystems such as nfs (w/ root squashing), vfat,
ntfs, cifs, exfat, etc. When directories need to be created or files
moved by mergerfs if the filesystem returns errors due to not
supporting certain POSIX filesystem features it could result in the
core functions failing.
Since mergerfs is not generally used with non-POSIX filesystems this
has not been a problem for users and there are some checks for known
edgecases but it is possible some are not accounted for. If use with a
edge cases but it is possible some are not accounted for. If use with a
filesystem results in issues please [file a
ticket](https://github.com/trapexit/mergerfs/issues) with the details.
## What types of storage devices does mergerfs work with?
Since mergerfs works at the filesystem level rather than device level
there is no compatibility concerns based on the physical storage
devices involved.
## Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
[https://www.snapraid.it](https://www.snapraid.it)
Yes. They are completely unrelated pieces of software that just happen
to work well together.
## Can I use mergerfs without nonraid? nonraid without mergerfs?
[https://github.com/qvr/nonraid](https://github.com/qvr/nonraid)
Yes. They are completely unrelated pieces of software that just happen
to work well together.
@@ -80,16 +92,8 @@ pool.
## Can mergerfs run via Docker, Podman, Kubernetes, etc.
Yes. With Docker you'll need to include `--cap-add=SYS_ADMIN
--device=/dev/fuse --security-opt=apparmor:unconfined` or similar with
other container runtimes. You should also be running it as root or
given sufficient caps to allow mergerfs to change user and group
identity as well as have root like filesystem permissions. This
ability is critical to how mergerfs works.
Also, as mentioned by [hotio](https://hotio.dev/containers/mergerfs),
with Docker you should probably be mounting with `bind-propagation`
set to `slave`.
Yes. [See installation
page.](../setup/installation.md#podman-docker-oci-containers)
## How does mergerfs interact with user namespaces?
@@ -29,6 +29,17 @@ If it isn't clear to you then you should use `pfrd` as described in
the [quickstart guide](../quickstart.md).
## Why are there no policies based on file size?
Because it is not possible to know the final size of a file at creation
time.
See [How does mergerfs handle moving and copying of
files?](technical_behavior_and_limitations.md#how-does-mergerfs-handle-moving-and-copying-of-files)
and [Intro to Filesystems - Creating a
file](../intro_to_filesystems.md#creating-a-file).
## Why is pfrd the default create policy?
Originally the default was `epmfs` however it was found to cause
@@ -1,56 +0,0 @@
# Recommendations and Warnings
## What should mergerfs NOT be used for?
* Situations where you need large amounts of contiguous space beyond
that available on any singular device. Such as putting 10GiB file on
2 6GiB filesystems.
* databases: Even if the database stored data in separate files
(mergerfs wouldn't offer much otherwise) the higher latency of the
indirection will really harm performance. If it is a lightly used
sqlite3 database then it should be fine.
* VM images: For the same reasons as databases. VM images are accessed
very aggressively and mergerfs will introduce a lot of extra latency.
* As replacement for RAID: mergerfs is just for pooling branches. If
you need device performance aggregation or high availability you
should stick with RAID. However, it is fine to put a filesystem
which is on a RAID setup in mergerfs.
**However, if using [passthrough](../config/passthrough.md) the
performance related issues above are less likely to be a concern. Best
to do testing for your specific use case.**
## It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
[mhddfs](https://github.com/trapexit/mhddfs) manages running as
`root` by calling
[getuid()](https://github.com/trapexit/mhddfs/blob/cae96e6251dd91e2bdc24800b4a18a74044f6672/src/main.c#L319)
and if it returns `0` then it will
[chown](http://linux.die.net/man/1/chown) the file. Not only is that a
race condition but it doesn't handle other situations. Rather than
attempting to simulate POSIX ACL behavior the proper way to manage
this is to use [seteuid](http://linux.die.net/man/2/seteuid) and
[setegid](http://linux.die.net/man/2/setegid), in effect, becoming the
user making the original call, and perform the action as them. This is
what mergerfs does and why mergerfs should always run as root.
In Linux setreuid syscalls apply only to the thread. glibc hides this
away by using realtime signals to inform all threads to change
credentials. Taking after Samba, mergerfs uses
`syscall(SYS_setreuid,...)` to set the callers credentials for that
thread only. Jumping back to `root` as necessary should escalated
privileges be needed (for instance: to clone paths between
filesystems).
For non-Linux systems, mergerfs uses a read-write lock and changes
credentials only when necessary. If multiple threads are to be user X
then only the first one will need to change the processes
credentials. So long as the other threads need to be user X they will
take a readlock allowing multiple threads to share the
credentials. Once a request comes in to run as user Y that thread will
attempt a write lock and change to Y's credentials when it can. If the
ability to give writers priority is supported then that flag will be
used so threads trying to change credentials don't starve. This isn't
the best solution but should work reasonably well assuming there are
few users.
@@ -68,6 +68,11 @@ underlying data. For more details as to why that is see the docs on
[inodecalc](../config/inodecalc.md).
## Are there any size limitations to files, directories, etc.?
Whatever the limits are of the underlying filesystems.
## Do reflink, FICLONE, or FICLONERANGE work?
Unfortunately not. FUSE, the technology mergerfs is based on, does not
@@ -146,7 +151,7 @@ You can remove the reserve by running: `tune2fs -m 0 <device>`
## I notice massive slowdowns of writes when enabling cache.files.
When file caching is enabled in any form (`cache.files!=off`) it will
When file caching is enabled in any form (`cache.files!=off`) it may
issue `getxattr` requests for `security.capability` prior to _every
single write_. This will usually result in performance degradation,
especially when using a network filesystem (such as NFS or SMB.)
@@ -178,20 +183,21 @@ disadvantages to each one.
A FUSE based solution has all the downsides of FUSE:
- Higher IO latency due to the trips in and out of kernel space
- Higher general overhead due to trips in and out of kernel space
- Double caching when using page caching
- Misc limitations due to FUSE's design
* Higher IO latency due to the trips in and out of kernel space
(though now minimized with passthrough IO)
* Higher general overhead due to trips in and out of kernel space
* Double caching when using page caching
* Misc limitations due to FUSE's design
But FUSE also has a lot of upsides:
- Easier to offer a cross platform solution
- Easier forward and backward compatibility
- Easier updates for users
- Easier and faster release cadence
- Allows more flexibility in design and features
- Overall easier to write, secure, and maintain
- Much lower barrier to entry (getting code into the kernel takes a
* Easier to offer a cross platform solution
* Easier forward and backward compatibility
* Easier updates for users
* Easier and faster release cadence
* Allows more flexibility in design and features
* Overall easier to write, secure, and maintain
* Much lower barrier to entry (getting code into the kernel takes a
lot of time and effort initially)
@@ -220,45 +226,56 @@ removed to simplify the codebase.
## How does mergerfs handle credentials?
mergerfs is a multithreaded application in order to handle requests
from the kernel concurrently. Each FUSE message has a header with
certain details about the request including the process ID (pid) of
the requesting application, the process' effective user id (uid), and
group id (gid). To ensure proper POSIX filesystem behavior and
security mergerfs must change its identity to match that of the
requester when performing the certain functions on the underlying
filesystem. As required by standards most Unix/POSIX based systems a
process and all its threads are under the same uid and gid. However,
on Linux each thread **may** have its own credentials. This allows
mergerfs to be multithreaded and for each thread to change to the
credentials as required by the incoming message it is
handling. However, currently on FreeBSD this is not possible (though
there has been
[discussions](https://wiki.freebsd.org/Per-Thread%20Credentials)) and
as such must change the credentials of the whole application when
actioning messages. mergerfs does optimize this behavior by only
changing credentials and locking the thread to do so if the process is
currently not the same as what is necessary by the incoming
request. As a result of this design FreeBSD may experience more
contention and therefore lower performance than Linux.
group id (gid).
Additionally, mergerfs [utilizes a cache for supplemental
groups](../known_issues_bugs.md#supplemental-user-groups) due the the
high cost of querying that information.
FUSE and the kernel have two ways of managing permissions. A kernel
side `default_permissions` option and leaving it to the FUSE
server. When default permissions is enabled the kernel will do the
entitlement checks and only allow requests through which should be
allowed according to normal POSIX permissions. When not enabled it is
the responsibility of the FUSE server, in this case mergerfs, to do
whatever is necessary to manage entitlements.
Prior to mergerfs v2.42.0 it would enable `default_permissions` but
also leveraged the uid and gid available in each FUSE request. The
thread actioning the request would change its credentials to match
those of the requesting application to ensure permissions were
properly handled as well as dealing with some quirks of non-POSIX
compatible filesystems. However, this strategy had two major
issues. First, it was not compatible with the new `allow-idmap`
feature of FUSE which allows a filesystem to advertise it can be used
with id mapping which is often used with containers. Secondly, it
caused permission issues when the kernel would say something was
allowed but due to the way mergerfs was changing creds would
fail. This mostly came in the form of `chroot`ed setups like used in
containers. Neither of these tended to impact casual users but did
break some niche or power user use cases. Since the casual user won't
notice the subtle changes and it would enable new use cases it was
decided that the credential handling would change.
As of v2.42.0 mergerfs now runs as root more generally. Either
changing credentials when creating files (Linux) or chown'ing them
after creation (FreeBSD).
As a result of this change it is now necessary for the FUSE
`default_permissions` feature be used for proper entitlements
management. mergerfs does allow it to be disabled but it should only
be done so for debugging purposes.
## Does mergerfs support idmap?
Yes. At least in so far as it's been enabled now the FUSE itself
allows a filesystem to indicate it is allowed.
Yes, by setting [allow-idmap=true](../config/options.md) (which is the
default.)
Requires that
[kernel-permissions-check](../config/kernel-permissions-check.md) be
enabled (the default.)
If there are any usage issues contact the [author](../support.md).
## What happens if a branch filesystem blocks?
@@ -282,3 +299,8 @@ Every incoming request contains:
Naturally the mergerfs config as well as anything queriable from the
operating system or filesystems are also available.
## Why does running `mount -a` result in new instances of mergerfs?
See [lazy-umount-mountpoint](../config/lazy-umount-mountpoint.md)
@@ -1,5 +1,26 @@
# Usage and Functionality
## What should mergerfs NOT be used for?
* Situations where you need large amounts of contiguous space beyond
that available on any singular device. Such as putting 10GiB a file
on 2 6GiB filesystems.
* databases: Even if the database stored data in separate files
(mergerfs wouldn't offer much otherwise) the higher latency of the
indirection will harm performance. Though if it is a sqlite3
database then its likely fine.
* VM images: For the same reasons as databases. VM images are accessed
very aggressively and mergerfs will introduce a lot of extra latency.
* As replacement for RAID: mergerfs is just for pooling branches. If
you need device performance aggregation or high availability you
should stick with RAID. However, it is fine to put a filesystem
which is on a RAID setup in mergerfs.
**However, if using [passthrough](../config/passthrough.md) the
performance related issues above are less likely to be a concern. Best
to do testing for your specific use case.**
## What happens when file paths overlap?
It depends on the situation and the configuration of mergerfs. The
+55 -17
View File
@@ -1,14 +1,44 @@
# mergerfs - a featureful union filesystem
**mergerfs** is a
[FUSE](https://en.wikipedia.org/wiki/Filesystem_in_Userspace) based
[union filesystem](https://en.wikipedia.org/wiki/Union_mount) geared
towards simplifying storage and management of files across numerous
commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
**aufs**, **DrivePool**, etc.](project_comparisons.md) Allowing for
the aggregation of storage without the more rigid requirements and
failure conditions of [RAID and other
technologies](project_comparisons.md).
**mergerfs** is a [union
filesystem](https://en.wikipedia.org/wiki/Union_mount) that makes
multiple filesystems and/or directories appear as a single unified
directory. mergerfs is designed to simplify how you manage files
across multiple independent filesystems without the complexity,
fragility, or cost of RAID or similar storage aggregation
technologies.
mergerfs as a smart pooling layer allows you to combine any number of
[existing
filesystems](faq/usage_and_functionality.md#can-mergerfs-be-used-with-filesystems-which-already-have-data);
whether they are on hard drives, SSDs, network shares, or other
mounted storage; into what looks like one large filesystem, while
still maintaining direct access to each individual filesystem. Unlike
RAID, there's no rebuild process if a device fails. You only lose
those files that were on that specific filesystem. You can also add or
remove filesystems at any time without restructuring the pool.
mergerfs excels at cost-effective storage expansion, making it ideal
for media libraries, backups, archival data, and other write-sometimes,
read-often workloads where you need lots of space but don't want
the overhead of traditional storage aggregation technologies.
**Key advantages:**
* Mix and match filesystems of any size,
[type](faq/compatibility_and_integration.md#what-filesystems-can-be-used-as-branches),
or [underlying
device](faq/compatibility_and_integration.md#what-types-of-storage-devices-does-mergerfs-work-with)
* No parity calculations or rebuild times
* Does not require hard drives to be spinning if not in use
* Add or remove filesystems on the fly
* [Direct access to files](faq/usage_and_functionality.md#can-filesystems-still-be-used-directly-outside-of-mergerfs-while-pooled) on individual filesystems when needed
* Flexible [policies](config/functions_categories_policies.md) for controlling where new files are created
For users seeking alternatives to mhddfs, unionfs, aufs, or DrivePool,
mergerfs offers a mature, actively maintained solution with extensive
configuration options and documentation. See the [project comparisons
for more details.](project_comparisons.md)
## Features
@@ -19,7 +49,7 @@ technologies](project_comparisons.md).
* Ability to add or remove filesystems/paths without impacting the
rest of the data
* Unaffected by individual filesystem failure
* Configurable file selection and creation placement
* Configurable file creation placement
* File IO [passthrough](config/passthrough.md) for near native IO
performance (where supported)
* Works with filesystems of any size
@@ -28,7 +58,7 @@ technologies](project_comparisons.md).
* Ignore read-only filesystems when creating files
* [Hard links](faq/technical_behavior_and_limitations.md#do-hard-links-work)
* Hard link [copy-on-write / CoW](config/link-cow.md)
* [Runtime configurable](runtime_interface.md)
* [Runtime configurability](runtime_interface.md)
* Support for extended attributes (xattrs)
* Support for file attributes (chattr)
* Support for POSIX ACLs
@@ -36,19 +66,22 @@ technologies](project_comparisons.md).
## Non-features
* Read/write overlay on top of read-only filesystem like OverlayFS
* File whiteout
* RAID like redundancy (see [SnapRAID](https://www.snapraid.it) and
[NonRAID](https://github.com/qvr/nonraid))
* Redundancy
* LVM/RAID style block device aggregation
* Data integrity checks, snapshots, file versioning
* Read/write overlays on top of read-only filesystems (like OverlayFS)
* File whiteout
* Splitting of files across branches
* Active rebalancing of content
* [Actively attempt to keep hard drives spun down](faq/limit_drive_spinup.md)
## How it works
mergerfs logically merges multiple filesystem paths together. Not
block devices, not filesystem mounts, just paths. It acts as a proxy
to the underlying filesystem paths. Combining the behaviors of some
to the underlying filesystem. Combining the behaviors of some
functions and being a selector for others.
When the contents of a directory are requested mergerfs combines the
@@ -62,7 +95,7 @@ For functions which change attributes or remove the file the behavior
may be applied to all instances found.
The way in which mergerfs behaves is controlled by the
[config/options/settings](config/options.md). More specifically by
[config / options / settings](config/options.md). More specifically by
[policies](config/functions_categories_policies.md).
@@ -90,7 +123,6 @@ A + B = C
+-- file7 +-- file7 +-- file7
```
## Getting Started
Head to the [quick start guide](quickstart.md).
@@ -107,3 +139,9 @@ Head to the [quick start guide](quickstart.md).
like something is not explained sufficiently or missing please [ask
in one of the supported forums](support.md#contact-issue-submission)
and the docs will be updated.
* The search feature of MkDocs is not great. Searching for "literal
strings" will generally not work. Alernative solutions are being
investigated.
* While not intended for end users nor completely accurate some might
find the AI generated docs at https://deepwiki.com/trapexit/mergerfs
interesting or useful.
+49 -30
View File
@@ -4,10 +4,17 @@
### FreeBSD version
* FreeBSD doesn't have per thread credentials meaning threads must
block to change credentials as required by numerous filesystem
functions. This impacts performance.
* FreeBSD's FUSE implementation is lacking many features of Linux.
* [https://wiki.freebsd.org/FUSEFS](https://wiki.freebsd.org/FUSEFS)
* FreeBSD does not have per thread credentials nor Linux capabilities
like abilities meaning it runs as root and has to create files as
root then chown them rather than changing credentials and creating
the file as the uid:gid.
* FreeBSD does not have `getdents` like APIs for reading
directories. As a result it uses traditional `readdir`.
* FreeBSD's FUSE implementation is lacking many features of the Linux
version.
* runtime interface
* ioctl support
* IO passthrough
* statx
* lazy umount
@@ -16,26 +23,10 @@
* kernel symlink caching
* kernel readdir caching
* writeback caching
* idmap
* ...
### Supplemental user groups
#### Supplemental group caching
Due to the high cost of querying supplemental groups it is necessary
for mergerfs to cache the list. As a result if supplemental groups are
changed while mergerfs is running the cache may become stale. This
generally isn't a problem as these groups tend not to be changed
often. However, the cache has a default expiry of 1 hour and after 12
hours of no usage the cache entry will be removed altogether.
See [gidcache.expire-timeout and
gid-cache.remove-timeout](config/options.md) and the [runtime
interface](runtime_interface.md#commands) for forcing expiry and clearing of
the cache.
#### Host vs Container identity
While not a bug some users have found when using containers that
@@ -99,9 +90,9 @@ on [page caching](config/cache.md).
### Software using mmap
* software using sqlite3 w/ mmap
* Plex (for config)
* Jellyfin (for config)
* Emby (for config)
* Plex (for config, not media)
* Jellyfin (for config, not media)
* Emby (for config, not media)
* Radarr
* Sonarr
* Lidarr
@@ -113,17 +104,26 @@ on [page caching](config/cache.md).
operating system feature allowing files to be interacted with as if it
was normal memory. Support for `mmap` is **required** by a number of
projects. FUSE (and therefore mergerfs), in order to support `mmap`,
requires [page caching](config/cache.md) to be
enabled. IE `cache.files` must not be set to `off` (if using Linux before
v6.6 or mergerfs before v2.41.0.)
requires [page caching](config/cache.md) to be enabled. IE
`cache.files` must not be set to `off` (if using Linux before v6.6 or
mergerfs before v2.41.0.) In newer versions of mergerfs and Linux
`off` will work as it can now auto enable page caching (and therefore
`mmap`) as needed.
In newer versions of mergerfs and Linux `off` will work as it can now
auto enable page caching (and therefore `mmap`) as needed.
When `mmap` is needed but not available you may see `ENODEV` or `No
such device` errors though it will depend on the particular software.
Details on enabling `mmap` can be found at:
* [QuickStart](quickstart.md#configuration)
* [Config > cache.files](config/cache.md#cachefiles)
That said it is recommended that config and runtime files be stored on
SSDs on a regular filesystem for performance reasons. See [What should
mergerfs NOT be used
for?](faq/recommendations_and_warnings.md#what-should-mergerfs-not-be-used-for).
for?](faq/usage_and_functionality.md#what-should-mergerfs-not-be-used-for). Though
with [passthrough.io](config/passthrough.md) enabled that is less
of a concern.
Technically **sqlite3** does not require `mmap`, and in fact the
default is not to use it, but many programs enable the feature and
@@ -133,6 +133,25 @@ more compatible but could also be more performant in certain
situations.
### qbittorrent
Same `mmap` concerns as mentioned above but called out specifically
due to regularity of queries.
The default "Disk IO type" setting is "Memory mapped files" which
means it requires `mmap` support.
If qbittorrent errors when using mergerfs ensure that either "Disk IO
type" is set to "POSIX-compliant" or "Simple pread/pwrite"; or setup
mergerfs to support `mmap`.
References:
* [Software using mmap](#software-using-mmap)
* [QuickStart](quickstart.md#configuration)
* [Config > cache.files](config/cache.md#cachefiles)
### rsync
This isn't specific to mergerfs but when using `--sparse, -S` option
+9 -7
View File
@@ -1,15 +1,17 @@
# Tweaking Performance
`mergerfs` is effectively a filesystem proxy and therefore its
**mergerfs** is effectively a filesystem proxy and therefore its
theoretical max performance is that of the underlying devices
(ignoring caching performed by the kernel.) However, given it is a
FUSE based filesystem working from userspace there is an increase in
overhead relative to kernel based solutions. That said the performance
can match the theoretical max but it depends greatly on the system's
FUSE based filesystem working from userspace and it must combine the
behavior and information from multiple underlying branches there can
be an increase in overhead relative to other solutions. That said the
performance of certain functions, such as IO, can match the
theoretical max but it depends greatly on the system's
configuration. There are many things which can impact
performance. Device speeds and latency, network speeds and latency,
concurrency and parallel limits of the hardware, read/write sizes,
etc.
concurrency and parallel limits of the hardware, read/write sizes, the
number of branches, etc.
While some settings can impact performance they are all **functional**
in nature. Meaning they change mergerfs' behavior in some way. As a
@@ -27,7 +29,7 @@ before changing them to understand how functionality will change.
* enable [passthrough.io](config/passthrough.md) (likely to have the
biggest impact)
* change read or process [thread pools](config/threads.md)
* toggle [func.readdir](config/func_readdir.md)
* change [func.readdir](config/func_readdir.md)
* increase [readahead](config/readahead.md): `readahead=1024`
* disable `security-capability` and/or [xattr](config/xattr.md)
* increase cache timeouts [cache.attr](config/cache.md#cacheattr),
+43 -26
View File
@@ -7,11 +7,13 @@ There are no solutions, only trade-offs.
* [https://romanrm.net/mhddfs](https://romanrm.net/mhddfs)
mhddfs is, like mergerfs, a FUSE based filesystem which can pool
multiple filesystems together.
mhddfs had not been updated in over a decade and has known stability
and security issues. mergerfs provides a super set of mhddfs' features
and offers better performance. In fact, as of 2020, the author of
mhddfs has [moved to using
mergerfs.](https://romanrm.net/mhddfs#update)
and offers better performance. As of 2020 the author of mhddfs has
[moved to using mergerfs.](https://romanrm.net/mhddfs#update)
Below is an example of mhddfs and mergerfs setup to work similarly.
@@ -25,14 +27,18 @@ Below is an example of mhddfs and mergerfs setup to work similarly.
* [https://aufs.sourceforge.net](https://aufs.sourceforge.net)
* [https://en.wikipedia.org/wiki/Aufs](https://en.wikipedia.org/wiki/Aufs)
aufs, another union filesystem, is a kernel based overlay filesystem
with basic file creation placement policies.
While aufs still is maintained it failed to be included in the
mainline kernel and is no longer available in most Linux distros
making it harder to get installed for the average user.
While aufs can often offer better peak performance due to being
primarily kernel based, mergerfs provides more configurability and is
generally easier to use. mergerfs however does not offer the overlay /
copy-on-write (CoW) features which aufs has.
primarily kernel based (at least when `passthrough.io` is disabled),
mergerfs provides more configurability and is generally easier to
use. mergerfs however does not offer the overlay / copy-on-write (CoW)
features which aufs has.
## unionfs
@@ -40,7 +46,7 @@ copy-on-write (CoW) features which aufs has.
* [https://unionfs.filesystems.org](https://unionfs.filesystems.org)
unionfs for Linux is a "stackable unification file system" which
functions like many other union filesystems. unionfs has not been
functions like many other union filesystems. unionfs is no longer
maintained and was last released for Linux v3.14 back in 2014.
Documentation is sparse so a comparison of features is not possible
@@ -101,7 +107,7 @@ aggregate their storage. The failure of any one device will have no
impact on the other devices. The downside to mergerfs' technique is
the fact you don't actually have contiguous space as large as if you
used those other technologies. Meaning you can't create a file greater
than 1TB on a pool of 2 1TB filesystems.
than 1TB on a pool of two 1TB filesystems.
## BTRFS Single Data Profile
@@ -118,15 +124,20 @@ some abilities to recover data but this is not guaranteed.
* [RAID5](https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_5)
* [RAID6](https://en.wikipedia.org/wiki/Standard_RAID_levels#RAID_6)
RAID5, RAID6, and similar technologies aggregate multiple devices at
the block level and offer 1 or more live parity calculations that can
allow the pool to continue to run should 1 device fail and rebuild the
data once the device is replaced.
mergerfs offers no parity or redundancy features so in that regard the
technologies are not comparable. [SnapRAID](https://www.snapraid.it)
can be used in combination with mergerfs to provide redundancy. Unlike
traditional RAID5 or RAID6 SnapRAID works with drives of different
sizes and can have more than 2 parity drives. However, parity
calculations are not done in real-time.
or [nonraid](https://github.com/qvr/nonraid) can be used in
combination with mergerfs to provide redundancy. Unlike traditional
RAID5 or RAID6 SnapRAID works with drives of different sizes and can
have more than 2 parity drives. However, parity calculations are not
done in real-time. However, nonraid is realtime.
See [https://www.snapraid.it/compare](https://www.snapraid.it/compare)
for more details and comparisons.
For more details and comparison of SnapRAID to related technologies see [https://www.snapraid.it/compare](https://www.snapraid.it/compare).
## UnRAID
@@ -141,7 +152,9 @@ calculation can have higher peak performance. Some users also prefer
an open source solution.
For semi-static data mergerfs + [SnapRAID](http://www.snapraid.it)
provides a similar, but not real-time, solution.
provides a similar, but not real-time, solution. NonRAID (see below)
is a fork of UnRAID's parity calculation solution and can also be used
with mergerfs.
## NonRAID
@@ -163,6 +176,9 @@ be used with mergerfs for those looking for a unified view.
* [https://en.wikipedia.org/wiki/ZFS](https://en.wikipedia.org/wiki/ZFS)
ZFS is an advanced filesystem with many features including RAID5/6
like redundency.
mergerfs is very different from ZFS. mergerfs is intended to provide
flexible pooling of arbitrary filesystems (local or remote), of
arbitrary sizes, and arbitrary filesystems. Particularly in `write
@@ -173,6 +189,10 @@ can introduce a number of costs and limitations as described
[here](https://markmcb.com/2020/01/07/five-years-of-btrfs/), and
[here](https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
That said given mergerfs' flexibility it is common to see it used in
combination with ZFS where critical data is written to a RAIDZ pool
and bulk media stored on traditional filesystems pooled via mergerfs.
## ZFS AnyRAID
@@ -191,9 +211,9 @@ and there are no timelines or estimates for when it may be released.
* [https://stablebit.com](https://stablebit.com)
DrivePool works only on Windows so not as common an alternative as
other Linux solutions. If you want to use Windows then DrivePool is a
good option. Functionally the two projects work a bit
DrivePool is a commercial filesystem pooling technology for
Windows. If you are looking to use Windows then DrivePool is a good
option. Functionally the two projects work a bit
differently. DrivePool always writes to the filesystem with the most
free space and later rebalances. mergerfs does not currently offer
rebalance but chooses a branch at file/directory create
@@ -202,15 +222,12 @@ and has file pattern matching to further customize the
behavior. mergerfs, not having rebalancing does not have these
features, but similar features are planned for mergerfs v3. DrivePool
has builtin file duplication which mergerfs does not natively support
(but can be done via an external script.)
(but can be done via an external program.)
There are a lot of misc differences between the two projects but most
features in DrivePool can be replicated with external tools in
combination with mergerfs.
Additionally, DrivePool is a closed source commercial product vs
mergerfs a ISC licensed open source project.
## Plan9 binds
@@ -218,10 +235,10 @@ mergerfs a ISC licensed open source project.
Plan9 has the native ability to bind multiple paths/filesystems
together to create a setup similar to simplified union
filesystem. Such bind mounts choose files in a "first found" in the
order they are listed similar to mergerfs' `ff` policy. Similar, when
creating a file it will be created on the first directory in the
union.
filesystem. Such bind mounts choose files in a "first found" manner
based on the order they are configured similar to mergerfs' `ff`
policy. Similarly, when creating a file it will be created on the
first directory in the union.
Plan 9 isn't a widely used OS so this comparison is mostly academic.
+1 -1
View File
@@ -76,7 +76,7 @@ but if you run into problems it may be worth trying Samba/SMB.
**mergerfs settings:**
* `noforget` or `remember-nodes=SECONDS`
* `never-forget-nodes=true` or `remember-nodes=SECONDS`
* `inodecalc=path-hash`
* `lazy-umount-mountpoint=false`
-1
View File
@@ -11,7 +11,6 @@
* 1MB+ pre reader thread + inflight processing for messages
depending on [fuse-msg-size](config/fuse-msg-size.md)
* buffers allocated temporarily for reading directories
* [gidcache](faq/technical_behavior_and_limitations.md#how-does-mergerfs-handle-credentials)
* FUSE nodes
* noforget forgotten nodes
-2
View File
@@ -143,8 +143,6 @@ allows a value.
| user.mergerfs.cmd.gc | (empty) |Trigger a thorough garbage collection of certain pools of resources. The xattr value is not used. |
| user.mergerfs.cmd.gc1 | (empty) | Trigger a simple garbage collection of certain pools of resources. This is also done on a timer. |
| user.mergerfs.cmd.invalidate-all-nodes | (empty) | Attempts to invalidate FUSE file nodes. Primarily used for debugging. |
| user.mergerfs.cmd.invalidate-gid-cache | (empty) | Invalidates all entries in gid cache. Primarily used for debugging but can also be useful in cases where gid supplemental groups change. |
| user.mergerfs.cmd.clear-gid-cache | (empty) | Clears all entries in the gid cache. Primarily used for debugging but can also be useful in cases where gid supplemental groups change. |
```
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.cmd.gc /mnt/mergerfs/.mergerfs
+32
View File
@@ -106,6 +106,7 @@ nix-env -iA nixos.mergerfs
[Check your distro.](../related_projects.md#distributions-including-mergerfs)
## Static Linux Binaries
If your distro does not package mergerfs there are static binaries
@@ -117,3 +118,34 @@ Get the tarball from the [releases page](https://github.com/trapexit/mergerfs/re
wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-static-linux_<arch>.tar.gz
sudo tar xvf mergerfs-static-linux_<arch>.tar.gz -C /
```
## Podman, Docker, OCI Containers
[https://github.com/trapexit/mergerfs/pkgs/container/mergerfs](https://github.com/trapexit/mergerfs/pkgs/container/mergerfs)
```
podman pull ghcr.io/trapexit/mergerfs:TAG
# or
docker pull ghcr.io/trapexit/mergerfs:TAG
```
### rootful container runtimes
```
docker run --device=/dev/fuse --cap-add=SYS_ADMIN -v /mnt/to-merge:/mnt/to-merge:rshared -v /mnt/mergerfs:/mnt/mergerfs:z,shared ghcr.io/trapexit/mergerfs:TAG -f '/mnt/to-merge/*' '/mnt/mergerfs'
```
* `--device=/dev/fuse`: Pass in host FUSE device.
* `--cap-add=SYS_ADMIN`: Give proper permissions to mount.
* `-v /mnt/mergerfs:/mnt/mergerfs:z,shared`: `z` and `shared` will
allow the mount to be shared between the container and host and
therefore other containers.
* `-v /mnt/to-merge:/mnt/to-merge:rshared`: The mounts from the host
to merge within the container.
### rootless container runtimes
Not possible? `:shared` is not rejected but won't work the same with
rootless. Podman doesn't allow the creation of pods with shared mount
namespace in rootless mode.
+4 -4
View File
@@ -1,4 +1,4 @@
# Sponsorship and Donations
# Sponsorship, Donations, Support Contracts
[https://github.com/trapexit/support](https://github.com/trapexit/support)
@@ -12,6 +12,6 @@ and would like to support the project financially it would be very
much appreciated.
If you are using mergerfs commercially please consider sponsoring the
project to ensure it continues to be maintained and receive
updates. If custom features are needed feel free to [contact me
directly](mailto:support@spawn.link).
project or purchasing a support contract to ensure it continues to be
maintained and receive updates. [Reach out for more
details](mailto:support@spawn.link?subject=mergerfs+support+contract+inquiry).
+2 -2
View File
@@ -38,8 +38,8 @@ directly.](mailto:support@spawn.link)**
* Are the client apps using mergerfs running in a container?
* A `strace` of the app having problems:
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
* A `strace` of mergerfs while the program is trying to do whatever it
is failing to do:
* A `strace` of mergerfs **while** the program is trying to do
whatever it is failing to do:
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
* **Precise** directions on replicating the issue. Do not leave
**anything** out.
+2 -2
View File
@@ -6,7 +6,7 @@
like something is not explained sufficiently please [file a
ticket.](https://github.com/trapexit/mergerfs/issues)
* If a `libfuse` argument is not listed it probably shouldn't be used
and are unsupported.
and is unsupported.
* Ensure you're using the latest version. Especially before submitting
bug reports.
* Run mergerfs as `root`. mergerfs is designed and intended to be run
@@ -15,7 +15,7 @@
seem to skip branches, you get strange permission errors, etc. be
sure the underlying filesystems' permissions are all the same. Use
`mergerfs.fsck` to audit the filesystem for out of sync permissions.
* If you still have permission issues be sure you are using POSIX ACL
* If you still have permission issues be sure you are using POSIX
compliant filesystems. mergerfs doesn't generally make exceptions
for FAT, NTFS, or other non-POSIX filesystem.
* Unless using Linux v6.6 or above do **not** use `cache.files=off` if
@@ -6,20 +6,21 @@ current_branch="$(git branch --show-current)"
mike delete --all --push --allow-empty --ignore-remote-status
git checkout "master"
mike deploy --push "preview" "master"
mike deploy --push "latest" "master" --ignore-remote-status
for tag in $(git tag -l | sort --version-sort --reverse)
do
if git ls-tree -r --name-only "${tag}" | grep -q 'mkdocs.yml'; then
git checkout "${tag}"
mike deploy --push "${tag}"
mike deploy --push "${tag}" --ignore-remote-status
fi
done
latest="$(git tag -l | sort --version-sort --reverse | head -n 1)"
git checkout "${latest}"
mike alias --push "${latest}" "latest"
#latest="$(git tag -l | sort --version-sort --reverse | head -n 1)"
#git checkout "${latest}"
#mike alias --push "${latest}" "latest"
#mike alias --push "latest" "latest"
mike set-default --push "preview"
mike set-default --push "latest" --ignore-remote-status
git checkout "${current_branch}"
+1 -1
View File
@@ -95,6 +95,7 @@ nav:
- config/flush-on-close.md
- config/export-support.md
- config/kernel-permissions-check.md
- config/lazy-umount-mountpoint.md
- error_handling_and_logging.md
- resource_usage.md
- runtime_interface.md
@@ -112,7 +113,6 @@ nav:
- faq/usage_and_functionality.md
- faq/reliability_and_scalability.md
- faq/compatibility_and_integration.md
- faq/recommendations_and_warnings.md
- faq/technical_behavior_and_limitations.md
- faq/have_you_considered.md
- faq/limit_drive_spinup.md
+108
View File
@@ -0,0 +1,108 @@
#pragma once
#include <memory>
#include <utility>
template<typename T>
class AtomicSharedPtr
{
private:
std::shared_ptr<T> _ptr;
public:
AtomicSharedPtr() = default;
explicit
AtomicSharedPtr(std::shared_ptr<T> ptr_)
: _ptr(std::move(ptr_))
{
}
template<typename... Args>
explicit
AtomicSharedPtr(Args&&... args)
: _ptr(std::make_shared<T>(std::forward<Args>(args)...))
{
}
AtomicSharedPtr(const AtomicSharedPtr&) = delete;
AtomicSharedPtr& operator=(const AtomicSharedPtr&) = delete;
AtomicSharedPtr(AtomicSharedPtr&& other_) noexcept
: _ptr(std::move(other_._ptr))
{
}
AtomicSharedPtr&
operator=(AtomicSharedPtr&& other_) noexcept
{
if(this != &other_)
_ptr = std::move(other_._ptr);
return *this;
}
std::shared_ptr<T>
load() const
{
return std::atomic_load(&_ptr);
}
void
store(std::shared_ptr<T> desired_)
{
std::atomic_store(&_ptr, std::move(desired_));
}
std::shared_ptr<T>
exchange(std::shared_ptr<T> desired_)
{
return std::atomic_exchange(&_ptr, std::move(desired_));
}
bool
compare_exchange_strong(std::shared_ptr<T>& expected_,
std::shared_ptr<T> desired_)
{
return std::atomic_compare_exchange_strong(&_ptr,
&expected_,
std::move(desired_));
}
bool
compare_exchange_weak(std::shared_ptr<T>& expected_,
std::shared_ptr<T> desired_)
{
return std::atomic_compare_exchange_weak(&_ptr,
&expected_,
std::move(desired_));
}
bool
is_null() const
{
return !std::atomic_load(&_ptr);
}
void
reset()
{
std::atomic_store(&_ptr, std::shared_ptr<T>());
}
T*
get() const
{
return std::atomic_load(&_ptr).get();
}
template<typename... Args>
auto
operator()(Args&&... args) const ->
decltype(std::declval<T>()(std::forward<Args>(args)...))
{
auto ptr = load();
return (*ptr)(std::forward<Args>(args)...);
}
};
+4 -1
View File
@@ -18,7 +18,7 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
#include "strvec.hpp"
#include "tofrom_string.hpp"
#include "fs_path.hpp"
@@ -52,6 +52,9 @@ public:
Branch(const Branch&);
Branch(const u64 &default_minfreespace);
public:
Branch& operator=(const Branch&) = default;
public:
bool ro(void) const;
bool nc(void) const;
+3 -3
View File
@@ -23,7 +23,7 @@
#include "fs_glob.hpp"
#include "fs_is_rofs.hpp"
#include "fs_realpathize.hpp"
#include "int_types.h"
#include "base_types.h"
#include "num.hpp"
#include "str.hpp"
#include "syslog.hpp"
@@ -81,9 +81,8 @@ namespace l
u64 offset;
offset = s_.find_first_not_of("+<>-=");
if (offset == std::string::npos) {
if(offset == std::string::npos)
return;
}
if(offset > 1)
offset = 2;
@@ -154,6 +153,7 @@ namespace l
rv = l::parse_minfreespace(v[1],minfreespace_);
if(rv < 0)
return rv;
[[fallthrough]];
case 1:
rv = l::parse_mode(v[0],mode_);
if(rv < 0)
+10 -3
View File
@@ -30,6 +30,9 @@
#include <vector>
#include <memory>
#define MINFREESPACE_DEFAULT (4294967295ULL)
class Branches final : public ToFromString
{
public:
@@ -63,14 +66,18 @@ public:
public:
using Ptr = Branches::Impl::Ptr;
public:
u64 minfreespace = MINFREESPACE_DEFAULT;
private:
mutable std::mutex _mutex;
Ptr _impl;
public:
Branches(const u64 &default_minfreespace_)
: _impl(std::make_shared<Impl>(default_minfreespace_))
{}
Branches()
: _impl(std::make_shared<Impl>(minfreespace))
{
}
public:
int from_string(const std::string_view str) final;
+104
View File
@@ -0,0 +1,104 @@
#include "caps.hpp"
#if defined __linux__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <grp.h>
#include <linux/capability.h>
#include <linux/securebits.h>
static
int
capset(cap_user_header_t header_,
const cap_user_data_t data_)
{
return ::syscall(SYS_capset,header_,data_);
}
static
int
capget(cap_user_header_t header_,
cap_user_data_t data_)
{
return ::syscall(SYS_capget,header_,data_);
}
static
int
capset(int cap_bit_)
{
int rv;
struct __user_cap_header_struct header;
struct __user_cap_data_struct data[2];
header.version = _LINUX_CAPABILITY_VERSION_3;
header.pid = 0;
rv = capget(&header,data);
if(rv < 0)
return rv;
int word = cap_bit_ / 32;
int bit = cap_bit_ % 32;
data[word].permitted |= (1 << bit);
data[word].effective |= (1 << bit);
data[word].inheritable |= (1 << bit);
rv = capset(&header,data);
if(rv < 0)
return rv;
return 0;
}
int
caps::setup()
{
int rv;
rv = capset(CAP_DAC_OVERRIDE);
if(rv < 0)
return rv;
rv = capset(CAP_DAC_READ_SEARCH);
if(rv < 0)
return rv;
rv = capset(CAP_FOWNER);
if(rv < 0)
return rv;
rv = capset(CAP_CHOWN);
if(rv < 0)
return rv;
rv = capset(CAP_SETUID);
if(rv < 0)
return rv;
rv = capset(CAP_SETGID);
if(rv < 0)
return rv;
rv = prctl(PR_SET_SECUREBITS,
SECBIT_KEEP_CAPS | SECBIT_NO_SETUID_FIXUP);
if(rv < 0)
return -errno;
return 0;
}
#else
#include <errno.h>
int
caps::setup()
{
return -ENOSYS;
}
#endif
+6
View File
@@ -0,0 +1,6 @@
#pragma once
namespace caps
{
int setup();
}
-11
View File
@@ -42,16 +42,8 @@ namespace Category
public:
Action(Funcs &funcs_)
{
funcs.push_back(&funcs_.chmod);
funcs.push_back(&funcs_.chown);
funcs.push_back(&funcs_.link);
funcs.push_back(&funcs_.removexattr);
funcs.push_back(&funcs_.rename);
funcs.push_back(&funcs_.rmdir);
funcs.push_back(&funcs_.setxattr);
funcs.push_back(&funcs_.truncate);
funcs.push_back(&funcs_.unlink);
funcs.push_back(&funcs_.utimens);
}
};
@@ -78,12 +70,9 @@ namespace Category
public:
Search(Funcs &funcs_)
{
funcs.push_back(&funcs_.access);
funcs.push_back(&funcs_.getattr);
funcs.push_back(&funcs_.getxattr);
funcs.push_back(&funcs_.listxattr);
funcs.push_back(&funcs_.open);
funcs.push_back(&funcs_.readlink);
}
};
}
+78 -71
View File
@@ -37,7 +37,6 @@
#include <sys/stat.h>
#include <unistd.h>
#define MINFREESPACE_DEFAULT (4294967295ULL)
constexpr static const char CACHE_FILES_PROCESS_NAMES_DEFAULT[] =
"rtorrent|"
@@ -79,9 +78,9 @@ Config::CfgConfigFile::to_string() const
Config::Config()
:
allow_idmap(false),
allow_idmap(true),
async_read(true),
branches(minfreespace),
branches(),
branches_mount_timeout(0),
branches_mount_timeout_fail(false),
cache_attr(1),
@@ -95,8 +94,6 @@ Config::Config()
cache_writeback(false),
category(func),
config_file(),
congestion_threshold(fuse_cfg.congestion_threshold,0),
debug(fuse_cfg.debug,false),
direct_io_allow_mmap(true),
dropcacheonclose(false),
export_support(true),
@@ -105,9 +102,6 @@ Config::Config()
fsname(),
func(),
fuse_msg_size("1M"),
gid(fuse_cfg.gid,FUSE_CFG_INVALID_ID),
gid_cache_expire_timeout(60 * 60),
gid_cache_remove_timeout(60 * 60 * 12),
handle_killpriv(true),
handle_killpriv_v2(true),
ignorepponrename(false),
@@ -117,40 +111,48 @@ Config::Config()
link_cow(false),
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
log_metrics(false),
max_background(fuse_cfg.max_background,0),
minfreespace(MINFREESPACE_DEFAULT),
minfreespace(branches.minfreespace),
mountpoint(),
_mount(mountpoint),
_mountpoint(mountpoint),
moveonenospc(true),
nfsopenhack(NFSOpenHack::ENUM::OFF),
noforget(),
nullrw(false),
parallel_direct_writes(true),
passthrough_io(PassthroughIO::ENUM::OFF),
passthrough_max_stack_depth(fuse_cfg.passthrough_max_stack_depth,1),
pin_threads(fuse_cfg.pin_threads,"false"),
passthrough_max_stack_depth(fuse_cfg.passthrough_max_stack_depth),
pin_threads(fuse_cfg.pin_threads),
posix_acl(false),
process_thread_count(fuse_cfg.process_thread_count,-1),
process_thread_queue_depth(fuse_cfg.process_thread_queue_depth,2),
process_thread_count(fuse_cfg.process_thread_count),
process_thread_queue_depth(fuse_cfg.process_thread_queue_depth),
proxy_ioprio(false),
read_thread_count(fuse_cfg.read_thread_count,0),
read_thread_count(fuse_cfg.read_thread_count),
readahead(0),
readdir("seq"),
remember_nodes(fuse_cfg.remember_nodes,0),
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
scheduling_priority(-10),
security_capability(true),
srcmounts(branches),
statfs(StatFS::ENUM::BASE),
statfs_ignore(StatFSIgnore::ENUM::NONE),
symlinkify(false),
symlinkify_timeout(3600),
threads(fuse_cfg.read_thread_count),
uid(fuse_cfg.uid,FUSE_CFG_INVALID_ID),
umask(fuse_cfg.umask,FUSE_CFG_INVALID_UMASK),
version(MERGERFS_VERSION),
xattr(XAttr::ENUM::PASSTHROUGH),
_congestion_threshold(fuse_cfg.congestion_threshold),
_debug(fuse_cfg.debug),
_getattr_statx(getattr,statx),
_gid(fuse_cfg.gid),
_max_background(fuse_cfg.max_background),
_mount(mountpoint),
_mountpoint(mountpoint),
_never_forget_nodes(),
_noforget(),
_remember(fuse_cfg.remember_nodes),
_remember_nodes(fuse_cfg.remember_nodes),
_srcmounts(branches),
_threads(fuse_cfg.read_thread_count),
_uid(fuse_cfg.uid),
_umask(fuse_cfg.umask),
_version(MERGERFS_VERSION),
_initialized(false)
{
allow_idmap.ro =
@@ -159,7 +161,7 @@ Config::Config()
branches_mount_timeout_fail.ro =
cache_symlinks.ro =
cache_writeback.ro =
congestion_threshold.ro =
_congestion_threshold.ro =
direct_io_allow_mmap.ro =
export_support.ro =
fsname.ro =
@@ -167,26 +169,30 @@ Config::Config()
handle_killpriv.ro =
handle_killpriv_v2.ro =
kernel_permissions_check.ro =
max_background.ro =
_max_background.ro =
_mount.ro =
_mountpoint.ro =
nullrw.ro =
pid.ro =
pin_threads.ro =
posix_acl.ro =
process_thread_count.ro =
process_thread_queue_depth.ro =
read_thread_count.ro =
scheduling_priority.ro =
srcmounts.ro =
version.ro =
_srcmounts.ro =
_version.ro =
true;
congestion_threshold.ro =
gid.display =
max_background.ro =
threads.display =
_congestion_threshold.display =
_gid.display =
_max_background.display =
_mount.display =
uid.display =
umask.display =
_noforget.display =
_remember.display =
_srcmounts.display =
_threads.display =
_uid.display =
_umask.display =
false;
_map["allow-idmap"] = &allow_idmap;
@@ -211,61 +217,63 @@ Config::Config()
_map["category.create"] = &category.create;
_map["category.search"] = &category.search;
_map["config"] = &config_file;
_map["debug"] = &_debug;
_map["defaults"] = &_dummy;
_map["debug"] = &debug;
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
_map["direct-io"] = &_dummy;
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
_map["dropcacheonclose"] = &dropcacheonclose;
_map["export-support"] = &export_support;
_map["flush-on-close"] = &flushonclose;
_map["follow-symlinks"] = &follow_symlinks;
_map["fsname"] = &fsname;
_map["func.access"] = &func.access;
_map["func.chmod"] = &func.chmod;
_map["func.chown"] = &func.chown;
_map["func.access"] = &access;
_map["func.chmod"] = &chmod;
_map["func.chown"] = &chown;
_map["func.create"] = &func.create;
_map["func.getattr"] = &func.getattr;
_map["func.getxattr"] = &func.getxattr;
_map["func.getattr"] = &_getattr_statx;
_map["func.getxattr"] = &getxattr;
_map["func.link"] = &func.link;
_map["func.listxattr"] = &func.listxattr;
_map["func.listxattr"] = &listxattr;
_map["func.mkdir"] = &func.mkdir;
_map["func.mknod"] = &func.mknod;
_map["func.mknod"] = &mknod;
_map["func.open"] = &func.open;
_map["func.readdir"] = &readdir;
_map["func.readlink"] = &func.readlink;
_map["func.removexattr"] = &func.removexattr;
_map["func.readlink"] = &readlink;
_map["func.removexattr"] = &removexattr;
_map["func.rename"] = &func.rename;
_map["func.rmdir"] = &func.rmdir;
_map["func.setxattr"] = &func.setxattr;
_map["func.rmdir"] = &rmdir;
_map["func.setxattr"] = &setxattr;
_map["func.statx"] = &_getattr_statx;
_map["func.symlink"] = &func.symlink;
_map["func.truncate"] = &func.truncate;
_map["func.unlink"] = &func.unlink;
_map["func.utimens"] = &func.utimens;
_map["func.truncate"] = &truncate;
_map["func.unlink"] = &unlink;
_map["func.utimens"] = &utimens;
_map["fuse-msg-size"] = &fuse_msg_size;
_map["gid"] = &gid;
_map["gid-cache.expire-timeout"] = &gid_cache_expire_timeout;
_map["gid-cache.remove-timeout"] = &gid_cache_remove_timeout;
_map["gid"] = &_gid;
_map["gid-cache.expire-timeout"] = &_dummy;
_map["gid-cache.remove-timeout"] = &_dummy;
_map["handle-killpriv"] = &handle_killpriv;
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
_map["hard-remove"] = &_dummy;
_map["ignorepponrename"] = &ignorepponrename;
_map["inodecalc"] = &inodecalc;
_map["kernel-permissions-check"] = &kernel_permissions_check;
_map["kernel-cache"] = &_dummy;
_map["kernel-permissions-check"] = &kernel_permissions_check;
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
_map["link-exdev"] = &link_exdev;
_map["link-cow"] = &link_cow;
_map["link-exdev"] = &link_exdev;
_map["log.metrics"] = &log_metrics;
_map["minfreespace"] = &minfreespace;
_map["mount"] = &_mount;
_map["mountpoint"] = &_mountpoint;
_map["moveonenospc"] = &moveonenospc;
_map["negative-entry"] = &_dummy;
_map["never-forget-nodes"] = &_never_forget_nodes;
_map["nfsopenhack"] = &nfsopenhack;
_map["no-splice-move"] = &_dummy;
_map["no-splice-read"] = &_dummy;
_map["no-splice-write"] = &_dummy;
_map["noforget"] = &noforget;
_map["noforget"] = &_noforget;
_map["nonempty"] = &_dummy;
_map["nullrw"] = &nullrw;
_map["parallel-direct-writes"] = &parallel_direct_writes;
@@ -279,23 +287,24 @@ Config::Config()
_map["proxy-ioprio"] = &proxy_ioprio;
_map["read-thread-count"] = &read_thread_count;
_map["readahead"] = &readahead;
_map["remember-nodes"] = &remember_nodes;
_map["remember"] = &_remember;
_map["remember-nodes"] = &_remember_nodes;
_map["rename-exdev"] = &rename_exdev;
_map["scheduling-priority"] = &scheduling_priority;
_map["security-capability"] = &security_capability;
_map["splice-move"] = &_dummy;
_map["splice-read"] = &_dummy;
_map["splice-write"] = &_dummy;
_map["srcmounts"] = &srcmounts;
_map["srcmounts"] = &_srcmounts;
_map["statfs"] = &statfs;
_map["statfs-ignore"] = &statfs_ignore;
_map["symlinkify"] = &symlinkify;
_map["symlinkify-timeout"] = &symlinkify_timeout;
_map["threads"] = &threads;
_map["uid"] = &uid;
_map["umask"] = &umask;
_map["threads"] = &_threads;
_map["uid"] = &_uid;
_map["umask"] = &_umask;
_map["use-ino"] = &_dummy;
_map["version"] = &version;
_map["version"] = &_version;
_map["xattr"] = &xattr;
}
@@ -371,7 +380,7 @@ Config::keys_listxattr(char *list_,
auto rv = fmt::format_to_n(list,size,
"user.mergerfs.{}\0",
key);
if(rv.out >= (list + size))
if(rv.out > (list + size))
return -ERANGE;
list += rv.size;
size -= rv.size;
@@ -389,6 +398,7 @@ Config::get(const std::string &key_,
key = str::replace(key_,'_','-');
i = _map.find(key);
if(i == _map.end())
return -ENOATTR;
@@ -434,8 +444,6 @@ Config::from_stream(std::istream &istrm_)
{
int rv;
std::string line;
std::string key;
std::string val;
Config::ErrVec new_errs;
while(std::getline(istrm_,line,'\n'))
@@ -444,13 +452,9 @@ Config::from_stream(std::istream &istrm_)
if(line.empty() || (line[0] == '#'))
continue;
str::splitkv(line,'=',&key,&val);
key = str::trim(key);
val = str::trim(val);
rv = set(key,val);
rv = set(line);
if(rv < 0)
new_errs.push_back({-rv,key+'='+val});
new_errs.push_back({-rv,line});
}
rv = (new_errs.empty() ? 0 : -EINVAL);
@@ -552,6 +556,9 @@ Config::Err::to_string() const
case ELOOP:
s = "too many levels of config";
break;
case ENOENT:
s = "no value set";
break;
default:
s = strerror(err);
break;
+51 -18
View File
@@ -16,13 +16,29 @@
#pragma once
#include "func_access.hpp"
#include "func_chmod.hpp"
#include "func_chown.hpp"
#include "func_getattr.hpp"
#include "func_getxattr.hpp"
#include "func_listxattr.hpp"
#include "func_mknod.hpp"
#include "func_readlink.hpp"
#include "func_removexattr.hpp"
#include "func_rmdir.hpp"
#include "func_setxattr.hpp"
#include "func_statx.hpp"
#include "func_truncate.hpp"
#include "func_unlink.hpp"
#include "func_utimens.hpp"
#include "branches.hpp"
#include "category.hpp"
#include "config_cachefiles.hpp"
#include "config_dummy.hpp"
#include "config_flushonclose.hpp"
#include "config_follow_symlinks.hpp"
#include "config_gidcache.hpp"
#include "config_getattr_statx.hpp"
#include "config_inodecalc.hpp"
#include "config_link_exdev.hpp"
#include "config_log_metrics.hpp"
@@ -109,6 +125,22 @@ public:
Config& operator=(const Config&);
public:
Func2::Access access{"all"};
Func2::Chmod chmod{"all"};
Func2::Chown chown{"all"};
Func2::GetAttr getattr{"cdfo"};
Func2::Getxattr getxattr{"ff"};
Func2::Listxattr listxattr{"ff"};
Func2::Mknod mknod{"ff"};
Func2::Readlink readlink{"ff"};
Func2::Removexattr removexattr{"all"};
Func2::Rmdir rmdir{"all"};
Func2::Setxattr setxattr{"all"};
Func2::Statx statx{"cdfo"};
Func2::Truncate truncate{"all"};
Func2::Unlink unlink{"all"};
Func2::Utimens utimens{"all"};
ConfigBOOL allow_idmap;
ConfigBOOL async_read;
Branches branches;
@@ -125,8 +157,6 @@ public:
ConfigBOOL cache_writeback;
Categories category;
CfgConfigFile config_file;
TFSRef<int> congestion_threshold;
TFSRef<bool> debug;
ConfigBOOL direct_io_allow_mmap;
ConfigBOOL dropcacheonclose;
ConfigBOOL export_support;
@@ -135,9 +165,6 @@ public:
ConfigSTR fsname;
Funcs func;
ConfigPageSize fuse_msg_size;
TFSRef<s64> gid;
GIDCacheExpireTimeout gid_cache_expire_timeout;
GIDCacheRemoveTimeout gid_cache_remove_timeout;
ConfigBOOL handle_killpriv;
ConfigBOOL handle_killpriv_v2;
ConfigBOOL ignorepponrename;
@@ -147,14 +174,10 @@ public:
ConfigBOOL link_cow;
LinkEXDEV link_exdev;
LogMetrics log_metrics;
TFSRef<int> max_background;
ConfigUINT64 minfreespace;
TFSRef<u64> minfreespace;
fs::path mountpoint;
TFSRef<fs::path> _mount;
TFSRef<fs::path> _mountpoint;
MoveOnENOSPC moveonenospc;
NFSOpenHack nfsopenhack;
CfgNoforget noforget;
ConfigBOOL nullrw;
ConfigBOOL parallel_direct_writes;
PassthroughIO passthrough_io;
@@ -168,23 +191,33 @@ public:
TFSRef<int> read_thread_count;
ConfigUINT64 readahead;
FUSE::ReadDir readdir;
TFSRef<s64> remember_nodes;
RenameEXDEV rename_exdev;
ConfigINT scheduling_priority;
ConfigBOOL security_capability;
SrcMounts srcmounts;
StatFS statfs;
StatFSIgnore statfs_ignore;
ConfigBOOL symlinkify;
ConfigS64 symlinkify_timeout;
TFSRef<int> threads;
TFSRef<s64> uid;
TFSRef<s64> umask;
ConfigROSTR version;
XAttr xattr;
private:
CfgDummy _dummy;
TFSRef<int> _congestion_threshold;
TFSRef<bool> _debug;
CfgDummy _dummy;
ConfigGetAttrStatx _getattr_statx;
TFSRef<s64> _gid;
TFSRef<int> _max_background;
TFSRef<fs::path> _mount;
TFSRef<fs::path> _mountpoint;
CfgNoforget _never_forget_nodes;
CfgNoforget _noforget;
TFSRef<s64> _remember;
TFSRef<s64> _remember_nodes;
SrcMounts _srcmounts;
TFSRef<int> _threads;
TFSRef<s64> _uid;
TFSRef<s64> _umask;
ConfigROSTR _version;
private:
bool _initialized;
+1 -7
View File
@@ -19,12 +19,6 @@
#pragma once
#include "enum.hpp"
#include "follow_symlinks_enum.hpp"
enum class FollowSymlinksEnum
{
NEVER,
DIRECTORY,
REGULAR,
ALL
};
typedef Enum<FollowSymlinksEnum> FollowSymlinks;
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "config.hpp"
#include "tofrom_string.hpp"
class ConfigGetAttrStatx : public ToFromString
{
private:
Func2::GetAttr &_getattr;
Func2::Statx &_statx;
public:
ConfigGetAttrStatx() = delete;
ConfigGetAttrStatx(Func2::GetAttr &getattr_,
Func2::Statx &statx_)
: _getattr(getattr_),
_statx(statx_)
{
}
std::string
to_string() const
{
return _getattr.to_string();
}
int
from_string(const std::string_view str_)
{
int rv;
rv = _getattr.from_string(str_);
if(rv < 0)
return rv;
rv = _statx.from_string(str_);
assert(rv == 0);
return rv;
}
};
-65
View File
@@ -1,65 +0,0 @@
#include "config_gidcache.hpp"
#include "gidcache.hpp"
#include "to_string.hpp"
#include "from_string.hpp"
GIDCacheExpireTimeout::GIDCacheExpireTimeout(const int i_)
{
GIDCache::expire_timeout = i_;
}
GIDCacheExpireTimeout::GIDCacheExpireTimeout(const std::string &s_)
{
from_string(s_);
}
std::string
GIDCacheExpireTimeout::to_string(void) const
{
return str::to(GIDCache::expire_timeout);
}
int
GIDCacheExpireTimeout::from_string(const std::string_view s_)
{
int rv;
rv = str::from(s_,&GIDCache::expire_timeout);
if(rv < 0)
return rv;
return 0;
}
GIDCacheRemoveTimeout::GIDCacheRemoveTimeout(const int i_)
{
GIDCache::remove_timeout = i_;
}
GIDCacheRemoveTimeout::GIDCacheRemoveTimeout(const std::string &s_)
{
from_string(s_);
}
std::string
GIDCacheRemoveTimeout::to_string(void) const
{
return str::to(GIDCache::remove_timeout);
}
int
GIDCacheRemoveTimeout::from_string(const std::string_view s_)
{
int rv;
rv = str::from(s_,&GIDCache::remove_timeout);
if(rv < 0)
return rv;
return 0;
}
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include "tofrom_string.hpp"
class GIDCacheExpireTimeout : public ToFromString
{
public:
GIDCacheExpireTimeout(const int i = 60 * 60);
GIDCacheExpireTimeout(const std::string &);
public:
std::string to_string(void) const final;
int from_string(const std::string_view) final;
};
class GIDCacheRemoveTimeout : public ToFromString
{
public:
GIDCacheRemoveTimeout(const int = 60 * 60 * 12);
GIDCacheRemoveTimeout(const std::string &);
public:
std::string to_string(void) const final;
int from_string(const std::string_view) final;
};
+1 -1
View File
@@ -44,7 +44,7 @@ public:
return *this;
}
operator const uint64_t() const
operator uint64_t() const
{
return _v;
}
+1 -1
View File
@@ -18,7 +18,7 @@
#include "fh.hpp"
#include "int_types.h"
#include "base_types.h"
class DirInfo : public FH
+14 -7
View File
@@ -15,27 +15,34 @@ public:
{
}
operator int()
operator int() const
{
return (_err.has_value() ? _err.value() : -ENOENT);
return (_err.has_value() ? _err.value() : 0);
}
// A rough heuristic for prioritizing errors. Success always
// overrides failures. ENOENT is lowest errno priority, then EROFS
// (which can be set due to branch mode = RO) then any other error.
Err&
operator=(int v_)
operator=(int new_err_)
{
if(!_err.has_value())
_err = ((v_ >= 0) ? 0 : v_);
else if(v_ >= 0)
_err = ((new_err_ >= 0) ? 0 : new_err_);
else if(new_err_ >= 0)
_err = 0;
else if(_err == -ENOENT)
_err = new_err_;
else if((_err == -EROFS) && (new_err_ != -ENOENT))
_err = new_err_;
return *this;
}
bool
operator==(int v_)
operator==(int other_)
{
if(_err.has_value())
return (_err.value() == v_);
return (_err.value() == other_);
return false;
}
};
+2 -2
View File
@@ -20,9 +20,9 @@
#include "fh.hpp"
#include "fs_path.hpp"
#include <mutex>
#include "base_types.h"
#include "int_types.h"
#include <mutex>
class FileInfo : public FH
+9
View File
@@ -0,0 +1,9 @@
#pragma once
enum class FollowSymlinksEnum
{
NEVER,
DIRECTORY,
REGULAR,
ALL
};
+2
View File
@@ -31,11 +31,13 @@ str::from(const std::string_view value_,
bool *bool_)
{
if((value_ == "true") ||
(value_ == "t") ||
(value_ == "1") ||
(value_ == "on") ||
(value_ == "yes"))
*bool_ = true;
ef((value_ == "false") ||
(value_ == "f") ||
(value_ == "0") ||
(value_ == "off") ||
(value_ == "no"))
+1 -1
View File
@@ -18,7 +18,7 @@
#include <string>
#include "int_types.h"
#include "base_types.h"
#define FS_ATTR_NONE (0)
#define FS_ATTR_CLEAR_IMMUTABLE (1 << 0)
+2 -2
View File
@@ -31,7 +31,7 @@ using std::string;
static
int
_get_fs_ioc_flags(const int fd,
int &flags)
int &flags)
{
int rv;
@@ -45,7 +45,7 @@ _get_fs_ioc_flags(const int fd,
static
int
_get_fs_ioc_flags(const string &file,
int &flags)
int &flags)
{
int fd;
int rv;
+63 -12
View File
@@ -27,6 +27,16 @@
#include "fs_xattr.hpp"
#include "ugid.hpp"
#include "fs_close.hpp"
#include "fs_fstat.hpp"
#include "fs_mkdirat.hpp"
#include "fs_openat.hpp"
#include "fs_fchown.hpp"
#include "fs_fchmod.hpp"
#include "fs_futimens.hpp"
#include "scope_guard.hpp"
static
bool
@@ -107,21 +117,62 @@ fs::clonepath(const fs::path &srcpath_,
return 0;
}
// WORK IN PROGRESS
static
int
fs::clonepath_as_root(const fs::path &srcpath_,
const fs::path &dstpath_,
const fs::path &relpath_,
const bool return_metadata_errors_)
_clonepath2(const int srcfd_,
const int dstfd_,
const fs::path &dirname_,
const bool return_metadata_errors_)
{
if(relpath_.empty())
return 0;
if(srcpath_ == dstpath_)
int rv;
int srcdirfd;
int dstdirfd;
struct stat st;
if(dirname_.empty())
return 0;
const ugid::SetRootGuard ugid_guard;
rv = fs::mkdirat(dstfd_,dirname_,0);
if(rv < 0)
return ((rv == -EEXIST) ? 0 : rv);
return fs::clonepath(srcpath_,
dstpath_,
relpath_,
return_metadata_errors_);
srcdirfd = fs::openat(srcfd_,dirname_,O_DIRECTORY);
if(srcdirfd < 0)
return srcdirfd;
DEFER { fs::close(srcdirfd); };
dstdirfd = fs::openat(dstfd_,dirname_,O_DIRECTORY);
if(dstdirfd < 0)
return dstdirfd;
DEFER { fs::close(dstdirfd); };
rv = fs::attr::copy(srcdirfd,dstdirfd,FS_ATTR_CLEAR_IMMUTABLE);
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
return rv;
rv = fs::xattr::copy(srcdirfd,dstdirfd);
if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv))
return rv;
rv = fs::fstat(srcdirfd,&st);
if(rv < 0)
return rv;
if(!S_ISDIR(st.st_mode))
return -ENOTDIR;
rv = fs::fchown_check_on_error(dstdirfd,st);
if(rv < 0)
return rv;
rv = fs::fchmod_check_on_error(dstdirfd,st);
if(rv < 0)
return rv;
rv = fs::futimens(dstdirfd,st);
if(rv < 0)
return rv;
return 0;
}
-5
View File
@@ -25,9 +25,4 @@ namespace fs
const fs::path &dstpath,
const fs::path &relpath,
const bool return_metadata_errors = false);
int clonepath_as_root(const fs::path &srcpath,
const fs::path &dstpath,
const fs::path &relpath,
const bool return_metadata_errors = false);
}
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
namespace fs
+1 -1
View File
@@ -20,7 +20,7 @@
#include "fs_copy_file_range.hpp"
#include "fs_fstat.hpp"
#include "int_types.h"
#include "base_types.h"
static
+1 -1
View File
@@ -16,7 +16,7 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
namespace fs
+1 -1
View File
@@ -16,7 +16,7 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
namespace fs
{
+1 -1
View File
@@ -103,7 +103,7 @@ fs::copyfile(const int src_fd_,
{
s64 rv;
int dst_fd;
struct stat src_st = {0};
struct stat src_st = {};
std::string dst_tmppath;
struct sigaction old_act;
struct sigaction new_act;
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
#include "fs_path.hpp"
#include <sys/stat.h>
+1 -1
View File
@@ -38,7 +38,7 @@ fs::glob(const string &pattern_,
vector<string> *strs_)
{
int flags;
glob_t gbuf = {0};
glob_t gbuf = {};
flags = (GLOB_BRACE|GLOB_ONLYDIR);
::glob(pattern_.c_str(),flags,NULL,&gbuf);
-2
View File
@@ -49,8 +49,6 @@ namespace fs
bool
is_rofs(const fs::path &path_)
{
ugid::SetRootGuard const ugid;
int fd;
std::string tmp_filepath;
+5 -14
View File
@@ -19,8 +19,7 @@
#pragma once
#include "to_neg_errno.hpp"
#include <string>
#include "to_cstr.hpp"
#include <sys/stat.h>
#include <sys/types.h>
@@ -29,25 +28,17 @@
namespace fs
{
template<typename PathType>
static
inline
int
lstat(const char *path_,
struct stat *st_)
lstat(const PathType &path_,
struct stat *st_)
{
int rv;
rv = ::lstat(path_,st_);
rv = ::lstat(to_cstr(path_),st_);
return ::to_neg_errno(rv);
}
static
inline
int
lstat(const std::string &path_,
struct stat *st_)
{
return fs::lstat(path_.c_str(),st_);
}
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include "fs_mkdir.hpp"
#include "ugid.hpp"
#if defined __linux__
namespace fs
{
template<typename T>
static
inline
int
mkdir_as(const ugid_t ugid_,
const T &path_,
const mode_t mode_)
{
const ugid::SetGuard _(ugid_);
return fs::mkdir(path_,mode_);
}
}
#elif defined __FreeBSD__
#include "fs_lchown.hpp"
namespace fs
{
template<typename T>
static
inline
int
mkdir_as(const ugid_t ugid_,
const T &path_,
const mode_t mode_)
{
int rv;
rv = fs::mkdir(path_,mode_);
if(rv < 0)
return rv;
fs::lchown(path_,ugid_.uid,ugid_.gid);
return 0;
}
}
#else
#error "Not Supported"
#endif
-35
View File
@@ -1,35 +0,0 @@
/*
ISC License
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "fs_mkdir.hpp"
#include "ugid.hpp"
namespace fs
{
template<typename T>
static
inline
int
mkdir_as_root(const T &path_,
const mode_t mode_)
{
const ugid::SetRootGuard guard;
return fs::mkdir(path_,mode_);
}
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include "to_neg_errno.hpp"
#include "fs_path.hpp"
#include <fcntl.h>
#include <sys/stat.h>
namespace fs
{
static
inline
int
mkdirat(const int dirfd_,
const char *pathname_,
const mode_t mode_)
{
int rv;
rv = ::mkdirat(dirfd_,pathname_,mode_);
return ::to_neg_errno(rv);
}
static
inline
int
mkdirat(const int dirfd_,
const std::string &pathname_,
const mode_t mode_)
{
return fs::mkdirat(dirfd_,
pathname_.c_str(),
mode_);
}
static
inline
int
mkdirat(const int dirfd_,
const fs::path &pathname_,
const mode_t mode_)
{
return fs::mkdirat(dirfd_,
pathname_.c_str(),
mode_);
}
}
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include "fs_acl.hpp"
#include "fs_mknod.hpp"
#include "ugid.hpp"
#if defined __linux__
namespace fs
{
template<typename T>
static
inline
int
mknod_as(const ugid_t ugid_,
const T &path_,
mode_t mode_,
dev_t dev_,
mode_t umask_)
{
if(not fs::acl::dir_has_defaults(path_))
mode_ &= ~umask_;
const ugid::SetGuard _(ugid_);
return fs::mknod(path_,mode_,dev_);
}
}
#elif defined __FreeBSD__
#include "fs_lchown.hpp"
namespace fs
{
template<typename T>
static
inline
int
mknod_as(const ugid_t ugid_,
const T &path_,
mode_t mode_,
dev_t dev_,
mode_t umask_)
{
int rv;
if(not fs::acl::dir_has_defaults(path_))
mode_ &= ~umask_;
rv = fs::mknod(path_,mode_,dev_);
fs::lchown(path_,ugid_.uid,ugid_.gid);
return rv;
}
}
#else
#error "Not Supported!"
#endif
+1 -3
View File
@@ -16,6 +16,7 @@
#include "fs_movefile_and_open.hpp"
#include "base_types.h"
#include "errno.hpp"
#include "fs_clonepath.hpp"
#include "fs_close.hpp"
@@ -30,7 +31,6 @@
#include "fs_rename.hpp"
#include "fs_stat.hpp"
#include "fs_unlink.hpp"
#include "int_types.h"
#include "policy.hpp"
#include "ugid.hpp"
@@ -137,8 +137,6 @@ fs::movefile_and_open_as_root(const Policy::Create &policy_,
const fs::path &fusepath_,
const int origfd_)
{
const ugid::Set ugid(0,0);
return fs::movefile_and_open(policy_,
branches_,
branchpath_,
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "fs_open.hpp"
#include "ugid.hpp"
// Linux can set ugid because it allows for credentials per
// thread. FreeBSD however does not and must create as root then
// chown. Another option for both platforms would be to create a temp
// file, chown, then rename to target. Unfortunately, depending on if
// the target filesystem is POSIX compliant or uses POSIX / extended
// ACLs it is not possible to know what is best.
#if defined __linux__
namespace fs
{
static
inline
int
open_as(const ugid_t ugid_,
const fs::path &path_,
const int flags_,
const mode_t mode_)
{
const ugid::SetGuard _(ugid_);
return fs::open(path_,flags_,mode_);
}
}
#elif defined __FreeBSD__
#include "fs_fchown.hpp"
namespace fs
{
static
inline
int
open_as(const ugid_t ugid_,
const fs::path &path_,
const int flags_,
const mode_t mode_)
{
int rv;
rv = fs::open(path_,flags_,mode_);
if(rv < 0)
return rv;
fs::fchown(rv,ugid_.uid,ugid_.gid);
return rv;
}
}
#else
#error "Not Supported!"
#endif
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma once
#include "int_types.h"
#include "base_types.h"
#include "to_neg_errno.hpp"
#include <unistd.h>
View File
+55 -6
View File
@@ -18,7 +18,10 @@
#pragma once
#include "follow_symlinks_enum.hpp"
#include "to_neg_errno.hpp"
#include "to_cstr.hpp"
#include "fs_lstat.hpp"
#include <string>
@@ -29,25 +32,71 @@
namespace fs
{
template<typename PathType>
static
inline
int
stat(const char *path_,
struct stat *st_)
stat(const PathType &path_,
struct stat *st_)
{
int rv;
rv = ::stat(path_,st_);
rv = ::stat(to_cstr(path_),st_);
return ::to_neg_errno(rv);
}
template<typename PathType>
static
inline
int
stat(const std::string &path_,
struct stat *st_)
stat(const PathType &path_,
struct stat *st_,
FollowSymlinksEnum follow_)
{
return fs::stat(path_.c_str(),st_);
int rv;
switch(follow_)
{
default:
case FollowSymlinksEnum::NEVER:
rv = fs::lstat(path_,st_);
return rv;
case FollowSymlinksEnum::DIRECTORY:
rv = fs::lstat(path_,st_);
if((rv >= 0) && S_ISLNK(st_->st_mode))
{
struct stat st;
rv = fs::stat(path_,&st);
if(rv < 0)
return rv;
if(S_ISDIR(st.st_mode))
*st_ = st;
}
return rv;
case FollowSymlinksEnum::REGULAR:
rv = fs::lstat(path_,st_);
if((rv >= 0) && S_ISLNK(st_->st_mode))
{
struct stat st;
rv = fs::stat(path_,&st);
if(rv < 0)
return rv;
if(S_ISREG(st.st_mode))
*st_ = st;
}
return rv;
case FollowSymlinksEnum::ALL:
rv = fs::stat(path_,st_);
if(rv < 0)
rv = fs::lstat(path_,st_);
return rv;
}
return -ENOENT;
}
}

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