Compare commits

...
6 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 9123d05834 fuse_lowlevel.c 2024-02-12 20:03:05 -06:00
trapexitandGitHub d68ad9ac01 Expand the preload docs (#1299) 2024-01-30 00:03:39 -06:00
trapexitandGitHub ae6c4f7c25 Rework mergerfs vs X section of readme (#1298) 2024-01-28 18:12:29 -06:00
trapexitandGitHub 87475098a1 Add credit to nohajc for preload idea (#1295) 2024-01-22 12:52:51 -06:00
trapexitandGitHub d4dc0701d6 Create ld_preload to reopen files on underlying filesystem (#1294) 2024-01-22 12:42:55 -06:00
0x6d61726bandGitHub c1c2f075ee Update README.md (#1283)
* Update README.md

Information added that mergerfs v2.35.0 and newer sets `allow_other` (but not 2.33.5, that is used by Debian 12.2)
2023-12-01 15:45:13 -06:00
13 changed files with 1010 additions and 258 deletions
+56 -41
View File
@@ -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 -1
View File
@@ -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
+22 -7
View File
@@ -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:
+228 -110
View File
@@ -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
View File
@@ -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);
}
+24 -5
View File
@@ -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
View File
@@ -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.
+1
View File
@@ -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
View File
@@ -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;
}