Skip to content

Commit

Permalink
Create LD preload to reopen files on underlying filesystem
Browse files Browse the repository at this point in the history
  • Loading branch information
trapexit committed Jan 21, 2024
1 parent c1c2f07 commit 8e4467f
Show file tree
Hide file tree
Showing 10 changed files with 548 additions and 15 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
ISC License

Copyright (c) 2023, Antonio SJ Musumeci <[email protected]>
Copyright (c) 2024, Antonio SJ Musumeci <[email protected]>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
Expand Down
33 changes: 24 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2016, Antonio SJ Musumeci <[email protected]>
# Copyright (c) 2024, Antonio SJ Musumeci <[email protected]>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
Expand Down Expand Up @@ -26,7 +26,7 @@ STRIP = strip
PANDOC = pandoc
SED = sed
RPMBUILD = rpmbuild
GIT2DEBCL = ./tools/git2debcl
GIT2DEBCL = ./buildtools/git2debcl
PKGCONFIG = pkg-config

GIT_REPO = 0
Expand Down Expand Up @@ -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} \
Expand All @@ -92,18 +96,21 @@ LDFLAGS := \
-pthread \
-lrt

# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
DESTDIR =
PREFIX = /usr/local
EXEC_PREFIX = $(PREFIX)
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
Expand Down Expand Up @@ -141,7 +148,7 @@ endif

.PHONY: version
version:
tools/update-version
./buildtools/update-version

build/stamp:
$(MKDIR) -p build/.src build/.tests
Expand All @@ -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
Expand All @@ -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)"
Expand All @@ -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"

Expand Down Expand Up @@ -225,14 +240,14 @@ endif
signed-deb:
$(MAKE) distclean
$(MAKE) debian-changelog
dpkg-source -b .
dpkg-buildpackage -nc
# dpkg-source -b .
dpkg-buildpackage --build=binary -nc

deb:
$(MAKE) distclean
$(MAKE) debian-changelog
dpkg-source -b .
dpkg-buildpackage -nc -uc -us
# dpkg-source -b .
dpkg-buildpackage --build=binary -nc -uc -us

.PHONY: rpm-clean
rpm-clean:
Expand All @@ -250,7 +265,7 @@ rpm: tarball

.PHONY: install-build-pkgs
install-build-pkgs:
tools/install-build-pkgs
./buildtools/install-build-pkgs

.PHONY: libfuse
libfuse:
Expand Down
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1288,6 +1288,52 @@ typedef char IOCTL_BUF[4096];
# TOOLING
## preload.so
EXPERIMENTAL
This preloadable library overrides the creation and opening of files
in order to simulate passthrough file IO. It catches the
open/creat/fopen calls, lets mergerfs do the call, queries mergerfs
for the branch the file exists on, and reopens the file on the underlying
filesystem. Meaning that you will get native read/write performance.
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.
While the library was written to account for a number of edgecases
there could be some yet accounted for so please report any oddities.
### general usage
```
LD_PRELOAD=/usr/lib/mergerfs/preload.so touch /mnt/mergerfs/filename
```

### Docker usage

Assume `/mnt/fs0` and `/mnt/fs1` are pooled with mergerfs at
`/mnt/mergerfs`.

Remember that you must bind into the container the original host paths
to the same locations otherwise the preload module will not be able to
find the files.

```
docker run \
-e LD_PRELOAD=/usr/lib/mergerfs/preload.so \
-v /usr/lib/mergerfs/preload.so:/usr/lib/mergerfs/preload.so:ro \
-v /mnt:/mnt \
ubuntu:latest \
bash
```

## 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
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
52 changes: 48 additions & 4 deletions man/mergerfs.1
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1724,6 +1722,52 @@ 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
This preloadable library overrides the creation and opening of files in
order to simulate passthrough file IO.
It catches the open/creat/fopen calls, lets mergerfs do the call,
queries mergerfs for the branch the file exists on, and reopens the file
on the underlying filesystem.
Meaning that you will get native read/write performance.
.PP
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.
.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.
.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]/mnt/mergerfs\f[R].
.PP
Remember that you must bind into the container the original host paths
to the same locations otherwise the preload module will not be able to
find the files.
.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 /mnt:/mnt \[rs]
ubuntu:latest \[rs]
bash
\f[R]
.fi
.SS Misc
.IP \[bu] 2
https://github.com/trapexit/mergerfs-tools
.RS 2
Expand Down
1 change: 1 addition & 0 deletions mergerfs.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 8e4467f

Please sign in to comment.