mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-19 16:16:48 +00:00
Compare commits
129
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bd3726cf8 | ||
|
|
18b72049a4 | ||
|
|
2f91276333 | ||
|
|
7c1bea4757 | ||
|
|
14f775a302 | ||
|
|
4622bc8160 | ||
|
|
5176458c7b | ||
|
|
06256e1120 | ||
|
|
ab27265347 | ||
|
|
13b4bf4649 | ||
|
|
dc5c7e95a6 | ||
|
|
ef825e0ff6 | ||
|
|
557a02115c | ||
|
|
73c9d86481 | ||
|
|
7ba1b8f332 | ||
|
|
31470ee458 | ||
|
|
0ef62afcbf | ||
|
|
2cd64960e3 | ||
|
|
fb534e5aba | ||
|
|
6e4b618376 | ||
|
|
802f9a0eaf | ||
|
|
04f2068adf | ||
|
|
d36688c752 | ||
|
|
e0cf997312 | ||
|
|
999b774dcb | ||
|
|
c0252fa5e6 | ||
|
|
50caf408f6 | ||
|
|
2fcd019f30 | ||
|
|
3fed8a12db | ||
|
|
17f7df7b3e | ||
|
|
0b552d11f8 | ||
|
|
c59c79f92c | ||
|
|
2e1d028516 | ||
|
|
316c971e6f | ||
|
|
ee4a8a18ac | ||
|
|
4675d8045b | ||
|
|
72fde7def8 | ||
|
|
7e8e279ed3 | ||
|
|
b021e73752 | ||
|
|
06f0d1b2d8 | ||
|
|
7eeee57043 | ||
|
|
21b8e376be | ||
|
|
353a5f7103 | ||
|
|
a863981746 | ||
|
|
71a939112d | ||
|
|
882634fa38 | ||
|
|
03a28d3ebe | ||
|
|
6e5b4aa40f | ||
|
|
28c36aea0e | ||
|
|
bcb45b917d | ||
|
|
59bec051fc | ||
|
|
9dba189b90 | ||
|
|
e8289f2cbe | ||
|
|
ea88c8ac56 | ||
|
|
2c177ddaeb | ||
|
|
bb674484b1 | ||
|
|
3f0ec90891 | ||
|
|
2338e7d8a9 | ||
|
|
8ec2d01d59 | ||
|
|
4996628e7f | ||
|
|
3c96b138f0 | ||
|
|
4d81f824a4 | ||
|
|
d90763fcd2 | ||
|
|
b3100540c6 | ||
|
|
5db592d4db | ||
|
|
695b1042f9 | ||
|
|
fe4c7faf4b | ||
|
|
da13bdff15 | ||
|
|
5b478446aa | ||
|
|
a376263d43 | ||
|
|
a0e10d7638 | ||
|
|
f5bb8f6c60 | ||
|
|
266a936208 | ||
|
|
15dd1cae2b | ||
|
|
867fb8ef46 | ||
|
|
b33a72ca5c | ||
|
|
2fa854dcc3 | ||
|
|
03145a0789 | ||
|
|
a2638a98b0 | ||
|
|
8188b13d73 | ||
|
|
46d3297d5e | ||
|
|
51af59ee59 | ||
|
|
8a961d56d9 | ||
|
|
2166f3537b | ||
|
|
8e0e88cd34 | ||
|
|
02cb3e2242 | ||
|
|
4b02d1c6c0 | ||
|
|
89bbd38ef2 | ||
|
|
4b8d68fb03 | ||
|
|
234f534204 | ||
|
|
08b11ea09d | ||
|
|
1b1d46a35c | ||
|
|
07db8d2ee9 | ||
|
|
332ca520aa | ||
|
|
e18fcc5714 | ||
|
|
d818892b93 | ||
|
|
8ab6bcf554 | ||
|
|
b3b8e793f9 | ||
|
|
965e26989c | ||
|
|
0caae4fbf0 | ||
|
|
4d1760cb69 | ||
|
|
22df87c86a | ||
|
|
72fb6e4aba | ||
|
|
9b046d9a34 | ||
|
|
d8918458bf | ||
|
|
d3a3f65e7b | ||
|
|
411d181473 | ||
|
|
caf7915003 | ||
|
|
4c5473d569 | ||
|
|
104f378c2c | ||
|
|
a364405cf0 | ||
|
|
960f5fdbba | ||
|
|
6813a51fce | ||
|
|
f7e86d90ce | ||
|
|
66d120c721 | ||
|
|
01973214bf | ||
|
|
af1edadf72 | ||
|
|
3bacaa6f7f | ||
|
|
b6e1a08aef | ||
|
|
47bd4259b8 | ||
|
|
dc486e79ce | ||
|
|
78551049d5 | ||
|
|
4aab008233 | ||
|
|
3a7a4a13e7 | ||
|
|
41b65ae9e8 | ||
|
|
2412ae7316 | ||
|
|
ec16d07fba | ||
|
|
37f10888a9 | ||
|
|
0db8a1ed6a |
-150
@@ -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
|
||||
@@ -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**
|
||||
|
||||
|
||||
@@ -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 }}"
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/)
|
||||
|
||||
Executable
+16
@@ -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/
|
||||
@@ -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,
|
||||
|
||||
@@ -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/ /
|
||||
|
||||
@@ -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/ /
|
||||
|
||||
@@ -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/ /
|
||||
|
||||
@@ -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/ /
|
||||
|
||||
@@ -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/ /
|
||||
|
||||
@@ -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/ /
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
@@ -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 *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
#include "fuse_msgbuf_t.h"
|
||||
|
||||
u32 msgbuf_get_pagesize();
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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_,
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+182
-177
@@ -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
@@ -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.
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace caps
|
||||
{
|
||||
int setup();
|
||||
}
|
||||
@@ -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
@@ -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"] = ¶llel_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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator const uint64_t() const
|
||||
operator uint64_t() const
|
||||
{
|
||||
return _v;
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "fh.hpp"
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
|
||||
|
||||
class DirInfo : public FH
|
||||
|
||||
+14
-7
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
|
||||
|
||||
namespace fs
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "fs_copy_file_range.hpp"
|
||||
#include "fs_fstat.hpp"
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
|
||||
|
||||
static
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
|
||||
|
||||
namespace fs
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
+1
-1
@@ -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
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -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
@@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
@@ -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_);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_,
|
||||
|
||||
@@ -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
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "base_types.h"
|
||||
#include "to_neg_errno.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
+55
-6
@@ -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
Reference in New Issue
Block a user