mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-04-21 12:31:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9123d05834 | ||
|
|
d68ad9ac01 | ||
|
|
ae6c4f7c25 | ||
|
|
87475098a1 | ||
|
|
d4dc0701d6 | ||
|
|
c1c2f075ee |
+56
-41
@@ -5,7 +5,7 @@ freebsd_task:
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
@@ -15,7 +15,7 @@ freebsd_task:
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
@@ -25,7 +25,7 @@ freebsd_task:
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
@@ -35,7 +35,7 @@ freebsd_task:
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ freebsd_task:
|
||||
# osx_instance:
|
||||
# image: catalina-base
|
||||
# script:
|
||||
# - tools/install-build-pkgs
|
||||
# - buildtools/install-build-pkgs
|
||||
# - gmake -j4
|
||||
|
||||
linux_task:
|
||||
@@ -54,7 +54,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -65,7 +65,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -76,7 +76,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -87,7 +87,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -98,7 +98,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -109,7 +109,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -120,7 +120,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -131,7 +131,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -142,7 +142,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
@@ -153,7 +153,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -165,7 +165,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -177,7 +177,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -189,7 +189,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -201,7 +201,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -213,7 +213,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -225,7 +225,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -237,7 +237,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -249,7 +249,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -261,7 +261,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -273,7 +273,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -285,7 +285,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -297,7 +297,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
@@ -309,7 +309,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -324,7 +324,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -339,7 +339,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -354,7 +354,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -369,7 +369,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -384,7 +384,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -399,7 +399,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -414,7 +414,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -429,7 +429,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -444,7 +444,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -459,7 +459,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -474,7 +474,22 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- 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
|
||||
@@ -489,7 +504,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
@@ -504,7 +519,7 @@ linux_task:
|
||||
memory: 4G
|
||||
timeout_in: 15m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- buildtools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Copyright (c) 2024, 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
# Copyright (c) 2024, 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
|
||||
@@ -26,7 +26,7 @@ STRIP = strip
|
||||
PANDOC = pandoc
|
||||
SED = sed
|
||||
RPMBUILD = rpmbuild
|
||||
GIT2DEBCL = ./tools/git2debcl
|
||||
GIT2DEBCL = ./buildtools/git2debcl
|
||||
PKGCONFIG = pkg-config
|
||||
|
||||
GIT_REPO = 0
|
||||
@@ -68,6 +68,10 @@ TESTS_DEPS = $(TESTS:tests/%.cpp=build/.tests/%.d)
|
||||
TESTS_DEPS += $(DEPS)
|
||||
|
||||
MANPAGE = mergerfs.1
|
||||
CFLAGS ?= ${OPT_FLAGS}
|
||||
CFLAGS := ${CFLAGS} \
|
||||
-Wall \
|
||||
-Wno-unused-result
|
||||
CXXFLAGS ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
@@ -92,6 +96,7 @@ LDFLAGS := \
|
||||
-pthread \
|
||||
-lrt
|
||||
|
||||
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
EXEC_PREFIX = $(PREFIX)
|
||||
@@ -99,11 +104,13 @@ DATAROOTDIR = $(PREFIX)/share
|
||||
DATADIR = $(DATAROOTDIR)
|
||||
BINDIR = $(EXEC_PREFIX)/bin
|
||||
SBINDIR = $(EXEC_PREFIX)/sbin
|
||||
LIBDIR = $(EXEC_PREFIX)/lib
|
||||
MANDIR = $(DATAROOTDIR)/man
|
||||
MAN1DIR = $(MANDIR)/man1
|
||||
|
||||
INSTALLBINDIR = $(DESTDIR)$(BINDIR)
|
||||
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
|
||||
INSTALLLIBDIR = $(DESTDIR)$(LIBDIR)/mergerfs
|
||||
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
|
||||
|
||||
.PHONY: all
|
||||
@@ -141,7 +148,7 @@ endif
|
||||
|
||||
.PHONY: version
|
||||
version:
|
||||
tools/update-version
|
||||
./buildtools/update-version
|
||||
|
||||
build/stamp:
|
||||
$(MKDIR) -p build/.src build/.tests
|
||||
@@ -153,6 +160,10 @@ build/.src/%.o: src/%.cpp
|
||||
build/.tests/%.o: tests/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
build/preload.so: build/stamp tools/preload.c
|
||||
$(CC) -shared -fPIC $(CFLAGS) $(CPPFLAGS) -o $@ tools/preload.c
|
||||
|
||||
preload: build/preload.so
|
||||
|
||||
.PHONY: clean
|
||||
clean: rpm-clean
|
||||
@@ -166,7 +177,7 @@ ifeq ($(GIT_REPO),1)
|
||||
endif
|
||||
|
||||
.PHONY: install
|
||||
install: install-base install-mount-tools install-man
|
||||
install: install-base install-mount-tools install-preload install-man
|
||||
|
||||
install-base: build/mergerfs
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
@@ -180,6 +191,10 @@ install-man: $(MANPAGE)
|
||||
$(MKDIR) -p "$(INSTALLMAN1DIR)"
|
||||
$(INSTALL) -v -m 0644 "man/$(MANPAGE)" "$(INSTALLMAN1DIR)/$(MANPAGE)"
|
||||
|
||||
install-preload: preload
|
||||
$(MKDIR) -p "$(INSTALLLIBDIR)"
|
||||
$(INSTALL) -v -m 444 "build/preload.so" "$(INSTALLLIBDIR)/preload.so"
|
||||
|
||||
install-strip: install-base
|
||||
$(STRIP) "$(INSTALLBINDIR)/mergerfs"
|
||||
|
||||
@@ -225,13 +240,13 @@ endif
|
||||
signed-deb:
|
||||
$(MAKE) distclean
|
||||
$(MAKE) debian-changelog
|
||||
dpkg-source -b .
|
||||
# dpkg-source -b .
|
||||
dpkg-buildpackage -nc
|
||||
|
||||
deb:
|
||||
$(MAKE) distclean
|
||||
$(MAKE) debian-changelog
|
||||
dpkg-source -b .
|
||||
# dpkg-source -b .
|
||||
dpkg-buildpackage -nc -uc -us
|
||||
|
||||
.PHONY: rpm-clean
|
||||
@@ -250,7 +265,7 @@ rpm: tarball
|
||||
|
||||
.PHONY: install-build-pkgs
|
||||
install-build-pkgs:
|
||||
tools/install-build-pkgs
|
||||
./buildtools/install-build-pkgs
|
||||
|
||||
.PHONY: libfuse
|
||||
libfuse:
|
||||
|
||||
@@ -307,8 +307,8 @@ These options are the same regardless of whether you use them with the
|
||||
* **splice_read**: deprecated - Does nothing.
|
||||
* **splice_write**: deprecated - Does nothing.
|
||||
* **splice_move**: deprecated - Does nothing.
|
||||
* **allow_other**: deprecated - mergerfs always sets this FUSE option
|
||||
as normal permissions can be used to limit access.
|
||||
* **allow_other**: deprecated - mergerfs v2.35.0 and newer sets this FUSE option
|
||||
automatically if running as root.
|
||||
* **use_ino**: deprecated - mergerfs should always control inode
|
||||
calculation so this is enabled all the time.
|
||||
|
||||
@@ -670,7 +670,7 @@ writable.
|
||||
Even though it's a more niche situation this hack breaks normal
|
||||
security and behavior and as such is `off` by default. If set to `git`
|
||||
it will only perform the hack when the path in question includes
|
||||
`/.git/`. `all` will result it applying anytime a readonly file which
|
||||
`/.git/`. `all` will result it applying anytime a read-only file which
|
||||
is empty is opened for writing.
|
||||
|
||||
|
||||
@@ -937,7 +937,7 @@ By default FUSE would issue a flush before the release of a file
|
||||
descriptor. This was considered a bit aggressive and a feature added
|
||||
to give the FUSE server the ability to choose when that happens.
|
||||
|
||||
Options:
|
||||
Options:
|
||||
* always
|
||||
* never
|
||||
* opened-for-write
|
||||
@@ -1288,6 +1288,102 @@ typedef char IOCTL_BUF[4096];
|
||||
|
||||
# TOOLING
|
||||
|
||||
## preload.so
|
||||
|
||||
EXPERIMENTAL
|
||||
|
||||
For some time there has been work to enable passthrough IO in
|
||||
FUSE. Passthrough IO would allow for near native performance with
|
||||
regards to reads and writes (at the expense of certain mergerfs
|
||||
features.) However, there have been several complications which have
|
||||
kept the feature from making it into the mainline Linux kernel. Until
|
||||
that feature is available there are two methods to provide similar
|
||||
functionality. One method is using the LD_PRELOAD feature of the
|
||||
dynamic linker. The other leveraging ptrace to intercept
|
||||
syscalls. Each has their disadvantages. At the moment only a preload
|
||||
based tool is available. A ptrace based tool may be developed later if
|
||||
there is a need.
|
||||
|
||||
`/usr/lib/mergerfs/preload.so`
|
||||
|
||||
This [preloadable
|
||||
library](https://man7.org/linux/man-pages/man8/ld.so.8.html#ENVIRONMENT)
|
||||
overrides the creation and opening of files in order to simulate
|
||||
passthrough file IO. It catches the open/creat/fopen calls, has
|
||||
mergerfs do the call, queries mergerfs for the branch the file exists
|
||||
on, reopens the file on the underlying filesystem and returns that
|
||||
instead. Meaning that you will get native read/write performance
|
||||
because mergerfs is no longer part of the workflow. Keep in mind that
|
||||
this also means certain mergerfs features that work by interrupting
|
||||
the read/write workflow, such as `moveonenospc`, will no longer work.
|
||||
|
||||
Also understand that this will only work on dynamically linked
|
||||
software. Anything statically compiled will not work. Many GoLang and
|
||||
Rust apps are statically compiled.
|
||||
|
||||
The library will not interfere with non-mergerfs filesystems. The
|
||||
library is written to always fallback to returning the mergerfs opened
|
||||
file on error.
|
||||
|
||||
While the library was written to account for a number of edgecases
|
||||
there could be some yet accounted for so please report any oddities.
|
||||
|
||||
Thank you to
|
||||
[nohajc](https://github.com/nohajc/mergerfs-io-passthrough) for
|
||||
prototyping the idea.
|
||||
|
||||
|
||||
### general usage
|
||||
|
||||
```sh
|
||||
LD_PRELOAD=/usr/lib/mergerfs/preload.so touch /mnt/mergerfs/filename
|
||||
```
|
||||
|
||||
### Docker usage
|
||||
|
||||
Assume `/mnt/fs0` and `/mnt/fs1` are pooled with mergerfs at `/media`.
|
||||
|
||||
All mergerfs branch paths *must* be bind mounted into the container at
|
||||
the same path as found on the host so the preload library can see them.
|
||||
|
||||
```sh
|
||||
docker run \
|
||||
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \
|
||||
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \
|
||||
-v /media:/data \
|
||||
-v /mnt:/mnt \
|
||||
ubuntu:latest \
|
||||
bash
|
||||
```
|
||||
|
||||
or more explicitly
|
||||
|
||||
```sh
|
||||
docker run \
|
||||
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \
|
||||
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \
|
||||
-v /media:/data \
|
||||
-v /mnt/fs0:/mnt/fs0 \
|
||||
-v /mnt/fs1:/mnt/fs1 \
|
||||
ubuntu:latest \
|
||||
bash
|
||||
```
|
||||
|
||||
### systemd unit
|
||||
|
||||
Use the `Environment` option to set the LD_PRELOAD variable.
|
||||
|
||||
* https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Command%20lines
|
||||
* https://serverfault.com/questions/413397/how-to-set-environment-variable-in-systemd-service
|
||||
|
||||
```
|
||||
[Service]
|
||||
Environment=LD_PRELOAD=/usr/lib/mergerfs/preload.so
|
||||
```
|
||||
|
||||
|
||||
## Misc
|
||||
|
||||
* https://github.com/trapexit/mergerfs-tools
|
||||
* mergerfs.ctl: A tool to make it easier to query and configure mergerfs at runtime
|
||||
* mergerfs.fsck: Provides permissions and ownership auditing and the ability to fix them
|
||||
@@ -2039,15 +2135,16 @@ first directory will be placed on the same branch because it is
|
||||
preserving paths.
|
||||
|
||||
This catches a lot of new users off guard but changing the default
|
||||
would break the setup for many existing users. If you do not care
|
||||
about path preservation and wish your files to be spread across all
|
||||
your filesystems change to `mfs` or similar policy as described
|
||||
above. If you do want path preservation you'll need to perform the
|
||||
manual act of creating paths on the filesystems you want the data to
|
||||
land on before transferring your data. Setting `func.mkdir=epall` can
|
||||
simplify managing path preservation for `create`. Or use
|
||||
`func.mkdir=rand` if you're interested in just grouping together
|
||||
directory content by filesystem.
|
||||
would break the setup for many existing users and this policy is the
|
||||
safest policy as it will not change the general layout of the existing
|
||||
filesystems. If you do not care about path preservation and wish your
|
||||
files to be spread across all your filesystems change to `mfs` or
|
||||
similar policy as described above. If you do want path preservation
|
||||
you'll need to perform the manual act of creating paths on the
|
||||
filesystems you want the data to land on before transferring your
|
||||
data. Setting `func.mkdir=epall` can simplify managing path
|
||||
preservation for `create`. Or use `func.mkdir=rand` if you're
|
||||
interested in just grouping together directory content by filesystem.
|
||||
|
||||
|
||||
#### Do hardlinks work?
|
||||
@@ -2069,6 +2166,16 @@ mergerfs pool that includes all the paths you need if you want links
|
||||
to work.
|
||||
|
||||
|
||||
#### Does FICLONE or FICLONERANGE work?
|
||||
|
||||
Unfortunately not. FUSE, the technology mergerfs is based on, does not
|
||||
support the `clone_file_range` feature needed for it to work. mergerfs
|
||||
won't even know such a request is made. The kernel will simply return
|
||||
an error back to the application making the request.
|
||||
|
||||
Should FUSE gain the ability mergerfs will be updated to support it.
|
||||
|
||||
|
||||
#### Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
||||
|
||||
Yes. They are completely unrelated pieces of software.
|
||||
@@ -2187,103 +2294,6 @@ traditional read/write fallback to be provided. The splice code was
|
||||
removed to simplify the codebase.
|
||||
|
||||
|
||||
#### Why use mergerfs over mhddfs?
|
||||
|
||||
mhddfs is no longer maintained and has some known stability and
|
||||
security issues (see below). mergerfs provides a superset of mhddfs'
|
||||
features and should offer the same or maybe better performance.
|
||||
|
||||
Below is an example of mhddfs and mergerfs setup to work similarly.
|
||||
|
||||
`mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool`
|
||||
|
||||
`mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool`
|
||||
|
||||
|
||||
#### Why use mergerfs over aufs?
|
||||
|
||||
aufs is mostly abandoned and no longer available in many distros.
|
||||
|
||||
While aufs can offer better peak performance mergerfs provides more
|
||||
configurability and is generally easier to use. mergerfs however does
|
||||
not offer the overlay / copy-on-write (CoW) features which aufs and
|
||||
overlayfs have.
|
||||
|
||||
|
||||
#### Why use mergerfs over unionfs?
|
||||
|
||||
UnionFS is more like aufs than mergerfs in that it offers overlay /
|
||||
CoW features. If you're just looking to create a union of filesystems
|
||||
and want flexibility in file/directory placement then mergerfs offers
|
||||
that whereas unionfs is more for overlaying RW filesystems over RO
|
||||
ones.
|
||||
|
||||
|
||||
#### Why use mergerfs over overlayfs?
|
||||
|
||||
Same reasons as with unionfs.
|
||||
|
||||
|
||||
#### Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation / striping?
|
||||
|
||||
With simple JBOD / drive concatenation / stripping / RAID0 a single
|
||||
drive failure will result in full pool failure. mergerfs performs a
|
||||
similar function without the possibility of catastrophic failure and
|
||||
the difficulties in recovery. Drives may fail, however, all other data
|
||||
will continue to be accessible.
|
||||
|
||||
When combined with something like [SnapRaid](http://www.snapraid.it)
|
||||
and/or an offsite backup solution you can have the flexibility of JBOD
|
||||
without the single point of failure.
|
||||
|
||||
|
||||
#### Why use mergerfs over ZFS?
|
||||
|
||||
mergerfs is not intended to be a replacement for ZFS. mergerfs is
|
||||
intended to provide flexible pooling of arbitrary filesystems (local
|
||||
or remote), of arbitrary sizes, and arbitrary filesystems. For `write
|
||||
once, read many` usecases such as bulk media storage. Where data
|
||||
integrity and backup is managed in other ways. In that situation ZFS
|
||||
can introduce a number of costs and limitations as described
|
||||
[here](http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html),
|
||||
[here](https://markmcb.com/2020/01/07/five-years-of-btrfs/), and
|
||||
[here](https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
|
||||
|
||||
|
||||
#### Why use mergerfs over UnRAID?
|
||||
|
||||
UnRAID is a full OS and its storage layer, as I understand, is
|
||||
proprietary and closed source. Users who have experience with both
|
||||
have said they prefer the flexibility offered by mergerfs and for some
|
||||
the fact it is free and open source is important.
|
||||
|
||||
There are a number of UnRAID users who use mergerfs as well though I'm
|
||||
not entirely familiar with the use case.
|
||||
|
||||
|
||||
#### Why use mergerfs over StableBit's DrivePool?
|
||||
|
||||
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
|
||||
differently. DrivePool always writes to the filesystem with the most
|
||||
free space and later rebalances. mergerfs does not offer rebalance but
|
||||
chooses a branch at file/directory create time. DrivePool's
|
||||
rebalancing can be done differently in any directory 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.)
|
||||
|
||||
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 OSS project.
|
||||
|
||||
|
||||
#### What should mergerfs NOT be used for?
|
||||
|
||||
* databases: Even if the database stored data in separate files
|
||||
@@ -2435,6 +2445,114 @@ 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.
|
||||
|
||||
# mergerfs versus X
|
||||
|
||||
#### mhddfs
|
||||
|
||||
mhddfs had not been maintained for some time and has some known
|
||||
stability and security issues. mergerfs provides a superset of mhddfs'
|
||||
features and should offer the same or better performance.
|
||||
|
||||
Below is an example of mhddfs and mergerfs setup to work similarly.
|
||||
|
||||
`mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool`
|
||||
|
||||
`mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool`
|
||||
|
||||
|
||||
#### aufs
|
||||
|
||||
aufs is mostly abandoned and no longer available in most Linux distros.
|
||||
|
||||
While aufs can offer better peak performance mergerfs provides more
|
||||
configurability and is generally easier to use. mergerfs however does
|
||||
not offer the overlay / copy-on-write (CoW) features which aufs has.
|
||||
|
||||
|
||||
#### unionfs-fuse
|
||||
|
||||
unionfs-fuse is more like aufs than mergerfs in that it offers overlay /
|
||||
copy-on-write (CoW) features. If you're just looking to create a union
|
||||
of filesystems and want flexibility in file/directory placement then
|
||||
mergerfs offers that whereas unionfs is more for overlaying read/write
|
||||
filesystems over read-only ones.
|
||||
|
||||
|
||||
#### overlayfs
|
||||
|
||||
overlayfs is similar to aufs and unionfs-fuse in that it also is
|
||||
primarily used to layer a read/write filesystem over one or more
|
||||
read-only filesystems. It does not have the ability to spread
|
||||
files/directories across numerous filesystems.
|
||||
|
||||
|
||||
#### RAID0, JBOD, drive concatenation, striping
|
||||
|
||||
With simple JBOD / drive concatenation / stripping / RAID0 a single
|
||||
drive failure will result in full pool failure. mergerfs performs a
|
||||
similar function without the possibility of catastrophic failure and
|
||||
the difficulties in recovery. Drives may fail but all other
|
||||
filesystems and their data will continue to be accessible.
|
||||
|
||||
The main practical difference with mergerfs 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 2TB file on a pool of 2 1TB
|
||||
filesystems.
|
||||
|
||||
When combined with something like [SnapRaid](http://www.snapraid.it)
|
||||
and/or an offsite backup solution you can have the flexibility of JBOD
|
||||
without the single point of failure.
|
||||
|
||||
|
||||
#### UnRAID
|
||||
|
||||
UnRAID is a full OS and its storage layer, as I understand, is
|
||||
proprietary and closed source. Users who have experience with both
|
||||
have often said they prefer the flexibility offered by mergerfs and
|
||||
for some the fact it is open source is important.
|
||||
|
||||
There are a number of UnRAID users who use mergerfs as well though I'm
|
||||
not entirely familiar with the use case.
|
||||
|
||||
For semi-static data mergerfs + [SnapRaid](http://www.snapraid.it)
|
||||
provides a similar solution.
|
||||
|
||||
|
||||
#### ZFS
|
||||
|
||||
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. For `write once, read
|
||||
many` usecases such as bulk media storage. Where data integrity and
|
||||
backup is managed in other ways. In those usecases ZFS can introduce a
|
||||
number of costs and limitations as described
|
||||
[here](http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html),
|
||||
[here](https://markmcb.com/2020/01/07/five-years-of-btrfs/), and
|
||||
[here](https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
|
||||
|
||||
|
||||
#### StableBit's DrivePool
|
||||
|
||||
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
|
||||
differently. DrivePool always writes to the filesystem with the most
|
||||
free space and later rebalances. mergerfs does not offer rebalance but
|
||||
chooses a branch at file/directory create time. DrivePool's
|
||||
rebalancing can be done differently in any directory 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.)
|
||||
|
||||
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 OSS project.
|
||||
|
||||
|
||||
# SUPPORT
|
||||
|
||||
|
||||
+31
-1
@@ -177,6 +177,19 @@ static pthread_key_t fuse_context_key;
|
||||
static pthread_mutex_t fuse_context_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int fuse_context_ref;
|
||||
|
||||
static
|
||||
int
|
||||
fuse_valid_type(uint32_t const m_)
|
||||
{
|
||||
return (S_ISREG(m_) ||
|
||||
S_ISDIR(m_) ||
|
||||
S_ISLNK(m_) ||
|
||||
S_ISCHR(m_) ||
|
||||
S_ISBLK(m_) ||
|
||||
S_ISFIFO(m_) ||
|
||||
S_ISSOCK(m_));
|
||||
}
|
||||
|
||||
/*
|
||||
Why was the nodeid:generation logic simplified?
|
||||
|
||||
@@ -1569,6 +1582,7 @@ fuse_lib_lookup(fuse_req_t req,
|
||||
{
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
reply_entry(req,&e,-ESTALE);
|
||||
syslog(LOG_ERR,". for nodeid %zu is stale",nodeid);
|
||||
return;
|
||||
}
|
||||
dot->refctr++;
|
||||
@@ -1576,9 +1590,10 @@ fuse_lib_lookup(fuse_req_t req,
|
||||
}
|
||||
else if((name[1] == '.') && (name[2] == '\0'))
|
||||
{
|
||||
if(nodeid == 1)
|
||||
if(nodeid == FUSE_ROOT_ID)
|
||||
{
|
||||
reply_entry(req,&e,-ENOENT);
|
||||
syslog(LOG_ERR,".. for root node????");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1697,6 +1712,13 @@ fuse_lib_getattr(fuse_req_t req,
|
||||
free_path(f,hdr_->nodeid,path);
|
||||
}
|
||||
|
||||
if(buf.st_size > LLONG_MAX)
|
||||
syslog(LOG_ERR,"%s: %zu size > LLONG_MAX %zu",__FUNCTION__,hdr_->nodeid,buf.st_size);
|
||||
if(!fuse_valid_type(buf.st_mode))
|
||||
syslog(LOG_ERR,"%s: %zu invalid type %x",__FUNCTION__,hdr_->nodeid,buf.st_mode);
|
||||
if(hdr_->nodeid == FUSE_ROOT_ID && !S_ISDIR(buf.st_mode))
|
||||
syslog(LOG_ERR,"%s: rootid not type DIR %x",__FUNCTION__,buf.st_mode);
|
||||
|
||||
if(!err)
|
||||
{
|
||||
pthread_mutex_lock(&f->lock);
|
||||
@@ -1818,6 +1840,14 @@ fuse_lib_setattr(fuse_req_t req,
|
||||
f->fs->op.getattr(path,&stbuf,&timeout) :
|
||||
f->fs->op.fgetattr(fi,&stbuf,&timeout));
|
||||
|
||||
if(stbuf.st_size > LLONG_MAX)
|
||||
syslog(LOG_ERR,"%s: %zu size > LLONG_MAX %zu",__FUNCTION__,hdr_->nodeid,stbuf.st_size);
|
||||
if(!fuse_valid_type(stbuf.st_mode))
|
||||
syslog(LOG_ERR,"%s: %zu invalid type %x",__FUNCTION__,hdr_->nodeid,stbuf.st_mode);
|
||||
if(hdr_->nodeid == FUSE_ROOT_ID && !S_ISDIR(stbuf.st_mode))
|
||||
syslog(LOG_ERR,"%s: rootid not type DIR %x",__FUNCTION__,stbuf.st_mode);
|
||||
|
||||
|
||||
free_path(f,hdr_->nodeid,path);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,16 @@
|
||||
#include "fuse_pollhandle.h"
|
||||
#include "fuse_msgbuf.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/file.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef F_LINUX_SPECIFIC_BASE
|
||||
#define F_LINUX_SPECIFIC_BASE 1024
|
||||
@@ -320,6 +321,19 @@ fuse_reply_create(fuse_req_t req,
|
||||
return send_reply_ok(req, &buf, entrysize + sizeof(struct fuse_open_out));
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
fuse_valid_type(uint32_t const m_)
|
||||
{
|
||||
return (S_ISREG(m_) ||
|
||||
S_ISDIR(m_) ||
|
||||
S_ISLNK(m_) ||
|
||||
S_ISCHR(m_) ||
|
||||
S_ISBLK(m_) ||
|
||||
S_ISFIFO(m_) ||
|
||||
S_ISSOCK(m_));
|
||||
}
|
||||
|
||||
int
|
||||
fuse_reply_attr(fuse_req_t req,
|
||||
const struct stat *attr,
|
||||
@@ -333,6 +347,11 @@ fuse_reply_attr(fuse_req_t req,
|
||||
arg.attr_valid_nsec = 0;
|
||||
convert_stat(attr,&arg.attr);
|
||||
|
||||
if(arg.attr.size > LLONG_MAX)
|
||||
syslog(LOG_ERR,"fuse_reply_attr: attr.size > LLONG_MAX");
|
||||
if(!fuse_valid_type(arg.attr.mode))
|
||||
syslog(LOG_ERR,"fuse_reply_attr: invalid type %x",arg.attr.mode);
|
||||
|
||||
return send_reply_ok(req,&arg,size);
|
||||
}
|
||||
|
||||
|
||||
+220
-93
@@ -325,14 +325,12 @@ by the number of process threads plus read thread count.
|
||||
on file close.
|
||||
Mostly for when writeback is enabled or merging network filesystems.
|
||||
(default: opened-for-write)
|
||||
.RS 2
|
||||
.IP \[bu] 2
|
||||
\f[B]scheduling-priority=INT\f[R]: Set mergerfs\[cq] scheduling
|
||||
priority.
|
||||
Valid values range from -20 to 19.
|
||||
See \f[C]setpriority\f[R] man page for more details.
|
||||
(default: -10)
|
||||
.RE
|
||||
.IP \[bu] 2
|
||||
\f[B]fsname=STR\f[R]: Sets the name of the filesystem as seen in
|
||||
\f[B]mount\f[R], \f[B]df\f[R], etc.
|
||||
@@ -428,8 +426,8 @@ Use \f[C]async_read=false\f[R] instead.
|
||||
.IP \[bu] 2
|
||||
\f[B]splice_move\f[R]: deprecated - Does nothing.
|
||||
.IP \[bu] 2
|
||||
\f[B]allow_other\f[R]: deprecated - mergerfs always sets this FUSE
|
||||
option as normal permissions can be used to limit access.
|
||||
\f[B]allow_other\f[R]: deprecated - mergerfs v2.35.0 and newer sets this
|
||||
FUSE option automatically if running as root.
|
||||
.IP \[bu] 2
|
||||
\f[B]use_ino\f[R]: deprecated - mergerfs should always control inode
|
||||
calculation so this is enabled all the time.
|
||||
@@ -837,7 +835,7 @@ Even though it\[cq]s a more niche situation this hack breaks normal
|
||||
security and behavior and as such is \f[C]off\f[R] by default.
|
||||
If set to \f[C]git\f[R] it will only perform the hack when the path in
|
||||
question includes \f[C]/.git/\f[R].
|
||||
\f[C]all\f[R] will result it applying anytime a readonly file which is
|
||||
\f[C]all\f[R] will result it applying anytime a read-only file which is
|
||||
empty is opened for writing.
|
||||
.SH FUNCTIONS, CATEGORIES and POLICIES
|
||||
.PP
|
||||
@@ -1724,6 +1722,108 @@ IOCTL_INVALIDATE_ALL_NODES: Same as SIGUSR1.
|
||||
Send invalidation notifications to the kernel for all files causing
|
||||
unused files to be released from memory.
|
||||
.SH TOOLING
|
||||
.SS preload.so
|
||||
.PP
|
||||
EXPERIMENTAL
|
||||
.PP
|
||||
For some time there has been work to enable passthrough IO in FUSE.
|
||||
Passthrough IO would allow for near native performance with regards to
|
||||
reads and writes (at the expense of certain mergerfs features.) However,
|
||||
there have been several complications which have kept the feature from
|
||||
making it into the mainline Linux kernel.
|
||||
Until that feature is available there are two methods to provide similar
|
||||
functionality.
|
||||
One method is using the LD_PRELOAD feature of the dynamic linker.
|
||||
The other leveraging ptrace to intercept syscalls.
|
||||
Each has their disadvantages.
|
||||
At the moment only a preload based tool is available.
|
||||
A ptrace based tool may be developed later if there is a need.
|
||||
.PP
|
||||
\f[C]/usr/lib/mergerfs/preload.so\f[R]
|
||||
.PP
|
||||
This preloadable
|
||||
library (https://man7.org/linux/man-pages/man8/ld.so.8.html#ENVIRONMENT)
|
||||
overrides the creation and opening of files in order to simulate
|
||||
passthrough file IO.
|
||||
It catches the open/creat/fopen calls, has mergerfs do the call, queries
|
||||
mergerfs for the branch the file exists on, reopens the file on the
|
||||
underlying filesystem and returns that instead.
|
||||
Meaning that you will get native read/write performance because mergerfs
|
||||
is no longer part of the workflow.
|
||||
Keep in mind that this also means certain mergerfs features that work by
|
||||
interrupting the read/write workflow, such as \f[C]moveonenospc\f[R],
|
||||
will no longer work.
|
||||
.PP
|
||||
Also understand that this will only work on dynamically linked software.
|
||||
Anything statically compiled will not work.
|
||||
Many GoLang and Rust apps are statically compiled.
|
||||
.PP
|
||||
The library will not interfere with non-mergerfs filesystems.
|
||||
The library is written to always fallback to returning the mergerfs
|
||||
opened file on error.
|
||||
.PP
|
||||
While the library was written to account for a number of edgecases there
|
||||
could be some yet accounted for so please report any oddities.
|
||||
.PP
|
||||
Thank you to nohajc (https://github.com/nohajc/mergerfs-io-passthrough)
|
||||
for prototyping the idea.
|
||||
.SS general usage
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
LD_PRELOAD=/usr/lib/mergerfs/preload.so touch /mnt/mergerfs/filename
|
||||
\f[R]
|
||||
.fi
|
||||
.SS Docker usage
|
||||
.PP
|
||||
Assume \f[C]/mnt/fs0\f[R] and \f[C]/mnt/fs1\f[R] are pooled with
|
||||
mergerfs at \f[C]/media\f[R].
|
||||
.PP
|
||||
All mergerfs branch paths \f[I]must\f[R] be bind mounted into the
|
||||
container at the same path as found on the host so the preload library
|
||||
can see them.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
docker run \[rs]
|
||||
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \[rs]
|
||||
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \[rs]
|
||||
-v /media:/data \[rs]
|
||||
-v /mnt:/mnt \[rs]
|
||||
ubuntu:latest \[rs]
|
||||
bash
|
||||
\f[R]
|
||||
.fi
|
||||
.PP
|
||||
or more explicitly
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
docker run \[rs]
|
||||
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \[rs]
|
||||
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \[rs]
|
||||
-v /media:/data \[rs]
|
||||
-v /mnt/fs0:/mnt/fs0 \[rs]
|
||||
-v /mnt/fs1:/mnt/fs1 \[rs]
|
||||
ubuntu:latest \[rs]
|
||||
bash
|
||||
\f[R]
|
||||
.fi
|
||||
.SS systemd unit
|
||||
.PP
|
||||
Use the \f[C]Environment\f[R] option to set the LD_PRELOAD variable.
|
||||
.IP \[bu] 2
|
||||
https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Command%20lines
|
||||
.IP \[bu] 2
|
||||
https://serverfault.com/questions/413397/how-to-set-environment-variable-in-systemd-service
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
[Service]
|
||||
Environment=LD_PRELOAD=/usr/lib/mergerfs/preload.so
|
||||
\f[R]
|
||||
.fi
|
||||
.SS Misc
|
||||
.IP \[bu] 2
|
||||
https://github.com/trapexit/mergerfs-tools
|
||||
.RS 2
|
||||
@@ -2575,7 +2675,9 @@ Anything, files or directories, created in that first directory will be
|
||||
placed on the same branch because it is preserving paths.
|
||||
.PP
|
||||
This catches a lot of new users off guard but changing the default would
|
||||
break the setup for many existing users.
|
||||
break the setup for many existing users and this policy is the safest
|
||||
policy as it will not change the general layout of the existing
|
||||
filesystems.
|
||||
If you do not care about path preservation and wish your files to be
|
||||
spread across all your filesystems change to \f[C]mfs\f[R] or similar
|
||||
policy as described above.
|
||||
@@ -2605,6 +2707,16 @@ considered different devices.
|
||||
There is no way to link between them.
|
||||
You should mount in the highest directory in the mergerfs pool that
|
||||
includes all the paths you need if you want links to work.
|
||||
.SS Does FICLONE or FICLONERANGE work?
|
||||
.PP
|
||||
Unfortunately not.
|
||||
FUSE, the technology mergerfs is based on, does not support the
|
||||
\f[C]clone_file_range\f[R] feature needed for it to work.
|
||||
mergerfs won\[cq]t even know such a request is made.
|
||||
The kernel will simply return an error back to the application making
|
||||
the request.
|
||||
.PP
|
||||
Should FUSE gain the ability mergerfs will be updated to support it.
|
||||
.SS Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
||||
.PP
|
||||
Yes.
|
||||
@@ -2728,93 +2840,6 @@ best provide equivalent performance and in cases worse performance.
|
||||
Splice is not supported on other platforms forcing a traditional
|
||||
read/write fallback to be provided.
|
||||
The splice code was removed to simplify the codebase.
|
||||
.SS Why use mergerfs over mhddfs?
|
||||
.PP
|
||||
mhddfs is no longer maintained and has some known stability and security
|
||||
issues (see below).
|
||||
mergerfs provides a superset of mhddfs\[cq] features and should offer
|
||||
the same or maybe better performance.
|
||||
.PP
|
||||
Below is an example of mhddfs and mergerfs setup to work similarly.
|
||||
.PP
|
||||
\f[C]mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool\f[R]
|
||||
.PP
|
||||
\f[C]mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool\f[R]
|
||||
.SS Why use mergerfs over aufs?
|
||||
.PP
|
||||
aufs is mostly abandoned and no longer available in many distros.
|
||||
.PP
|
||||
While aufs can offer better peak performance mergerfs provides more
|
||||
configurability and is generally easier to use.
|
||||
mergerfs however does not offer the overlay / copy-on-write (CoW)
|
||||
features which aufs and overlayfs have.
|
||||
.SS Why use mergerfs over unionfs?
|
||||
.PP
|
||||
UnionFS is more like aufs than mergerfs in that it offers overlay / CoW
|
||||
features.
|
||||
If you\[cq]re just looking to create a union of filesystems and want
|
||||
flexibility in file/directory placement then mergerfs offers that
|
||||
whereas unionfs is more for overlaying RW filesystems over RO ones.
|
||||
.SS Why use mergerfs over overlayfs?
|
||||
.PP
|
||||
Same reasons as with unionfs.
|
||||
.SS Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation / striping?
|
||||
.PP
|
||||
With simple JBOD / drive concatenation / stripping / RAID0 a single
|
||||
drive failure will result in full pool failure.
|
||||
mergerfs performs a similar function without the possibility of
|
||||
catastrophic failure and the difficulties in recovery.
|
||||
Drives may fail, however, all other data will continue to be accessible.
|
||||
.PP
|
||||
When combined with something like SnapRaid (http://www.snapraid.it)
|
||||
and/or an offsite backup solution you can have the flexibility of JBOD
|
||||
without the single point of failure.
|
||||
.SS Why use mergerfs over ZFS?
|
||||
.PP
|
||||
mergerfs is not intended to be a replacement for ZFS.
|
||||
mergerfs is intended to provide flexible pooling of arbitrary
|
||||
filesystems (local or remote), of arbitrary sizes, and arbitrary
|
||||
filesystems.
|
||||
For \f[C]write once, read many\f[R] usecases such as bulk media storage.
|
||||
Where data integrity and backup is managed in other ways.
|
||||
In that situation ZFS can introduce a number of costs and limitations as
|
||||
described
|
||||
here (http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html),
|
||||
here (https://markmcb.com/2020/01/07/five-years-of-btrfs/), and
|
||||
here (https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
|
||||
.SS Why use mergerfs over UnRAID?
|
||||
.PP
|
||||
UnRAID is a full OS and its storage layer, as I understand, is
|
||||
proprietary and closed source.
|
||||
Users who have experience with both have said they prefer the
|
||||
flexibility offered by mergerfs and for some the fact it is free and
|
||||
open source is important.
|
||||
.PP
|
||||
There are a number of UnRAID users who use mergerfs as well though
|
||||
I\[cq]m not entirely familiar with the use case.
|
||||
.SS Why use mergerfs over StableBit\[cq]s DrivePool?
|
||||
.PP
|
||||
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 differently.
|
||||
DrivePool always writes to the filesystem with the most free space and
|
||||
later rebalances.
|
||||
mergerfs does not offer rebalance but chooses a branch at file/directory
|
||||
create time.
|
||||
DrivePool\[cq]s rebalancing can be done differently in any directory 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.)
|
||||
.PP
|
||||
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.
|
||||
.PP
|
||||
Additionally DrivePool is a closed source commercial product vs mergerfs
|
||||
a ISC licensed OSS project.
|
||||
.SS What should mergerfs NOT be used for?
|
||||
.IP \[bu] 2
|
||||
databases: Even if the database stored data in separate files (mergerfs
|
||||
@@ -2970,6 +2995,108 @@ If the ability to give writers priority is supported then that flag will
|
||||
be used so threads trying to change credentials don\[cq]t starve.
|
||||
This isn\[cq]t the best solution but should work reasonably well
|
||||
assuming there are few users.
|
||||
.SH mergerfs versus X
|
||||
.SS mhddfs
|
||||
.PP
|
||||
mhddfs had not been maintained for some time and has some known
|
||||
stability and security issues.
|
||||
mergerfs provides a superset of mhddfs\[cq] features and should offer
|
||||
the same or better performance.
|
||||
.PP
|
||||
Below is an example of mhddfs and mergerfs setup to work similarly.
|
||||
.PP
|
||||
\f[C]mhddfs -o mlimit=4G,allow_other /mnt/drive1,/mnt/drive2 /mnt/pool\f[R]
|
||||
.PP
|
||||
\f[C]mergerfs -o minfreespace=4G,category.create=ff /mnt/drive1:/mnt/drive2 /mnt/pool\f[R]
|
||||
.SS aufs
|
||||
.PP
|
||||
aufs is mostly abandoned and no longer available in most Linux distros.
|
||||
.PP
|
||||
While aufs can offer better peak performance mergerfs provides more
|
||||
configurability and is generally easier to use.
|
||||
mergerfs however does not offer the overlay / copy-on-write (CoW)
|
||||
features which aufs has.
|
||||
.SS unionfs-fuse
|
||||
.PP
|
||||
unionfs-fuse is more like aufs than mergerfs in that it offers overlay /
|
||||
copy-on-write (CoW) features.
|
||||
If you\[cq]re just looking to create a union of filesystems and want
|
||||
flexibility in file/directory placement then mergerfs offers that
|
||||
whereas unionfs is more for overlaying read/write filesystems over
|
||||
read-only ones.
|
||||
.SS overlayfs
|
||||
.PP
|
||||
overlayfs is similar to aufs and unionfs-fuse in that it also is
|
||||
primarily used to layer a read/write filesystem over one or more
|
||||
read-only filesystems.
|
||||
It does not have the ability to spread files/directories across numerous
|
||||
filesystems.
|
||||
.SS RAID0, JBOD, drive concatenation, striping
|
||||
.PP
|
||||
With simple JBOD / drive concatenation / stripping / RAID0 a single
|
||||
drive failure will result in full pool failure.
|
||||
mergerfs performs a similar function without the possibility of
|
||||
catastrophic failure and the difficulties in recovery.
|
||||
Drives may fail but all other filesystems and their data will continue
|
||||
to be accessible.
|
||||
.PP
|
||||
The main practical difference with mergerfs is the fact you don\[cq]t
|
||||
actually have contiguous space as large as if you used those other
|
||||
technologies.
|
||||
Meaning you can\[cq]t create a 2TB file on a pool of 2 1TB filesystems.
|
||||
.PP
|
||||
When combined with something like SnapRaid (http://www.snapraid.it)
|
||||
and/or an offsite backup solution you can have the flexibility of JBOD
|
||||
without the single point of failure.
|
||||
.SS UnRAID
|
||||
.PP
|
||||
UnRAID is a full OS and its storage layer, as I understand, is
|
||||
proprietary and closed source.
|
||||
Users who have experience with both have often said they prefer the
|
||||
flexibility offered by mergerfs and for some the fact it is open source
|
||||
is important.
|
||||
.PP
|
||||
There are a number of UnRAID users who use mergerfs as well though
|
||||
I\[cq]m not entirely familiar with the use case.
|
||||
.PP
|
||||
For semi-static data mergerfs + SnapRaid (http://www.snapraid.it)
|
||||
provides a similar solution.
|
||||
.SS ZFS
|
||||
.PP
|
||||
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.
|
||||
For \f[C]write once, read many\f[R] usecases such as bulk media storage.
|
||||
Where data integrity and backup is managed in other ways.
|
||||
In those usecases ZFS can introduce a number of costs and limitations as
|
||||
described
|
||||
here (http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html),
|
||||
here (https://markmcb.com/2020/01/07/five-years-of-btrfs/), and
|
||||
here (https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
|
||||
.SS StableBit\[cq]s DrivePool
|
||||
.PP
|
||||
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 differently.
|
||||
DrivePool always writes to the filesystem with the most free space and
|
||||
later rebalances.
|
||||
mergerfs does not offer rebalance but chooses a branch at file/directory
|
||||
create time.
|
||||
DrivePool\[cq]s rebalancing can be done differently in any directory 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.)
|
||||
.PP
|
||||
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.
|
||||
.PP
|
||||
Additionally DrivePool is a closed source commercial product vs mergerfs
|
||||
a ISC licensed OSS project.
|
||||
.SH SUPPORT
|
||||
.PP
|
||||
Filesystems are complex and difficult to debug.
|
||||
|
||||
@@ -35,6 +35,7 @@ make install PREFIX=%{_prefix} DESTDIR=%{buildroot}
|
||||
/usr/bin/mergerfs
|
||||
/usr/bin/mergerfs-fusermount
|
||||
/sbin/mount.mergerfs
|
||||
/usr/lib/mergerfs/preload.so
|
||||
%doc %{_mandir}/*
|
||||
|
||||
%changelog
|
||||
|
||||
+427
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
typedef char IOCTL_BUF[4096];
|
||||
#define IOCTL_APP_TYPE 0xDF
|
||||
#define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF)
|
||||
|
||||
#define LOAD_FUNC(func) \
|
||||
do \
|
||||
{ \
|
||||
if(!_libc_##func) \
|
||||
_libc_##func = (func##_func_t)dlsym(RTLD_NEXT,#func); \
|
||||
assert(_libc_##func != NULL); \
|
||||
} \
|
||||
while(0)
|
||||
|
||||
typedef int (*open_func_t)(const char*, int, ...);
|
||||
typedef int (*open64_func_t)(const char*, int, ...);
|
||||
typedef int (*openat_func_t)(int, const char*, int, ...);
|
||||
typedef int (*openat64_func_t)(int, const char*, int, ...);
|
||||
typedef int (*creat_func_t)(const char*, mode_t);
|
||||
typedef int (*creat64_func_t)(const char*, mode_t);
|
||||
typedef FILE* (*fopen_func_t)(const char*, const char*);
|
||||
typedef FILE* (*fopen64_func_t)(const char*, const char*);
|
||||
|
||||
static open_func_t _libc_open = NULL;
|
||||
static open64_func_t _libc_open64 = NULL;
|
||||
static openat_func_t _libc_openat = NULL;
|
||||
static openat64_func_t _libc_openat64 = NULL;
|
||||
static fopen_func_t _libc_fopen = NULL;
|
||||
static fopen64_func_t _libc_fopen64 = NULL;
|
||||
static creat_func_t _libc_creat = NULL;
|
||||
static creat64_func_t _libc_creat64 = NULL;
|
||||
|
||||
static
|
||||
int
|
||||
get_underlying_filepath(int fd_,
|
||||
char *filepath_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
strcpy(filepath_,"fullpath");
|
||||
rv = ioctl(fd_,IOCTL_FILE_INFO,filepath_);
|
||||
if(rv == -1)
|
||||
return -1;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
strip_exec(const char *orig_mode_,
|
||||
char *new_mode_)
|
||||
{
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
for(i = j = 0; orig_mode_[i]; i++)
|
||||
{
|
||||
if(orig_mode_[i] == 'x')
|
||||
continue;
|
||||
new_mode_[j++] = orig_mode_[i];
|
||||
}
|
||||
|
||||
new_mode_[j] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
open(const char *pathname_,
|
||||
int flags_,
|
||||
...)
|
||||
{
|
||||
int rv;
|
||||
int fd;
|
||||
mode_t mode;
|
||||
struct stat st;
|
||||
|
||||
LOAD_FUNC(open);
|
||||
|
||||
mode = 0;
|
||||
if(flags_ & O_CREAT)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args,flags_);
|
||||
mode = va_arg(args,mode_t);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
fd = _libc_open(pathname_,flags_,mode);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
if(flags_ & (O_DIRECTORY|O_PATH))
|
||||
return fd;
|
||||
rv = fstat(fd,&st);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
if((st.st_mode & S_IFMT) != S_IFREG)
|
||||
return fd;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
flags_ &= ~(O_EXCL|O_CREAT);
|
||||
rv = _libc_open(real_pathname,flags_,mode);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
open64(const char *pathname_,
|
||||
int flags_,
|
||||
...)
|
||||
{
|
||||
int rv;
|
||||
int fd;
|
||||
mode_t mode;
|
||||
struct stat st;
|
||||
|
||||
LOAD_FUNC(open64);
|
||||
|
||||
mode = 0;
|
||||
if(flags_ & O_CREAT)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args,flags_);
|
||||
mode = va_arg(args,mode_t);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
fd = _libc_open64(pathname_,flags_,mode);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
if(flags_ & (O_DIRECTORY|O_PATH))
|
||||
return fd;
|
||||
rv = fstat(fd,&st);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
if((st.st_mode & S_IFMT) != S_IFREG)
|
||||
return fd;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
flags_ &= ~(O_EXCL|O_CREAT);
|
||||
rv = _libc_open64(real_pathname,flags_,mode);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
openat(int dirfd_,
|
||||
const char *pathname_,
|
||||
int flags_,
|
||||
...)
|
||||
{
|
||||
int rv;
|
||||
int fd;
|
||||
mode_t mode;
|
||||
struct stat st;
|
||||
|
||||
LOAD_FUNC(openat);
|
||||
|
||||
mode = 0;
|
||||
if(flags_ & O_CREAT)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args,flags_);
|
||||
mode = va_arg(args,mode_t);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
fd = _libc_openat(dirfd_,pathname_,flags_,mode);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
if(flags_ & (O_DIRECTORY|O_PATH))
|
||||
return fd;
|
||||
rv = fstat(fd,&st);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
if((st.st_mode & S_IFMT) != S_IFREG)
|
||||
return fd;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
flags_ &= ~(O_EXCL|O_CREAT);
|
||||
rv = _libc_openat(dirfd_,real_pathname,flags_,mode);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
openat64(int dirfd_,
|
||||
const char *pathname_,
|
||||
int flags_,
|
||||
...)
|
||||
{
|
||||
int rv;
|
||||
int fd;
|
||||
mode_t mode;
|
||||
struct stat st;
|
||||
|
||||
LOAD_FUNC(openat64);
|
||||
|
||||
mode = 0;
|
||||
if(flags_ & O_CREAT)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args,flags_);
|
||||
mode = va_arg(args,mode_t);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
fd = _libc_openat64(dirfd_,pathname_,flags_,mode);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
if(flags_ & (O_DIRECTORY|O_PATH))
|
||||
return fd;
|
||||
rv = fstat(fd,&st);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
if((st.st_mode & S_IFMT) != S_IFREG)
|
||||
return fd;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
flags_ &= ~(O_EXCL|O_CREAT);
|
||||
rv = _libc_openat64(dirfd_,real_pathname,flags_,mode);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
FILE*
|
||||
fopen(const char *pathname_,
|
||||
const char *mode_)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
FILE *f;
|
||||
FILE *f2;
|
||||
struct stat st;
|
||||
|
||||
LOAD_FUNC(fopen);
|
||||
|
||||
f = _libc_fopen(pathname_,mode_);
|
||||
if(f == NULL)
|
||||
return NULL;
|
||||
|
||||
fd = fileno(f);
|
||||
if(fd == -1)
|
||||
return f;
|
||||
|
||||
rv = fstat(fd,&st);
|
||||
if(rv == -1)
|
||||
return f;
|
||||
if((st.st_mode & S_IFMT) != S_IFREG)
|
||||
return f;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return f;
|
||||
|
||||
char new_mode[64];
|
||||
strip_exec(mode_,new_mode);
|
||||
f2 = _libc_fopen(real_pathname,new_mode);
|
||||
if(f2 == NULL)
|
||||
return f;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return f2;
|
||||
}
|
||||
|
||||
FILE*
|
||||
fopen64(const char *pathname_,
|
||||
const char *mode_)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
FILE *f;
|
||||
FILE *f2;
|
||||
struct stat st;
|
||||
|
||||
LOAD_FUNC(fopen64);
|
||||
|
||||
f = _libc_fopen64(pathname_,mode_);
|
||||
if(f == NULL)
|
||||
return NULL;
|
||||
|
||||
fd = fileno(f);
|
||||
if(fd == -1)
|
||||
return f;
|
||||
|
||||
rv = fstat(fd,&st);
|
||||
if(rv == -1)
|
||||
return f;
|
||||
if((st.st_mode & S_IFMT) != S_IFREG)
|
||||
return f;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return f;
|
||||
|
||||
char new_mode[64];
|
||||
strip_exec(mode_,new_mode);
|
||||
f2 = _libc_fopen64(real_pathname,new_mode);
|
||||
if(f2 == NULL)
|
||||
return f;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return f2;
|
||||
}
|
||||
|
||||
int
|
||||
creat(const char *pathname_,
|
||||
mode_t mode_)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
|
||||
LOAD_FUNC(creat);
|
||||
|
||||
fd = _libc_creat(pathname_,mode_);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
rv = _libc_creat(real_pathname,mode_);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
creat64(const char *pathname_,
|
||||
mode_t mode_)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
|
||||
LOAD_FUNC(creat64);
|
||||
|
||||
fd = _libc_creat64(pathname_,mode_);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
IOCTL_BUF real_pathname;
|
||||
rv = get_underlying_filepath(fd,real_pathname);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
rv = _libc_creat64(real_pathname,mode_);
|
||||
if(rv == -1)
|
||||
return fd;
|
||||
|
||||
close(fd);
|
||||
|
||||
return rv;
|
||||
}
|
||||
Reference in New Issue
Block a user