mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-04-21 12:31:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9123d05834 | ||
|
|
d68ad9ac01 |
@@ -1292,17 +1292,38 @@ typedef char IOCTL_BUF[4096];
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
This will only work on dynamically linked software. Anything
|
||||
statically compiled will not work. Many GoLang and Rust apps are
|
||||
statically compiled.
|
||||
`/usr/lib/mergerfs/preload.so`
|
||||
|
||||
The library will not interfere with non-mergerfs filesystems.
|
||||
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.
|
||||
@@ -1314,28 +1335,53 @@ 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
|
||||
`/mnt/mergerfs`.
|
||||
Assume `/mnt/fs0` and `/mnt/fs1` are pooled with mergerfs at `/media`.
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
|
||||
+64
-11
@@ -1726,18 +1726,41 @@ unused files to be released from memory.
|
||||
.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.
|
||||
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
|
||||
This will only work on dynamically linked software.
|
||||
\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.
|
||||
@@ -1754,22 +1777,52 @@ LD_PRELOAD=/usr/lib/mergerfs/preload.so touch /mnt/mergerfs/filename
|
||||
.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].
|
||||
mergerfs at \f[C]/media\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.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user