mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-20 00:17:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e085beb0e | ||
|
|
58abe58ce2 | ||
|
|
564bb8c364 | ||
|
|
2e3307bac8 | ||
|
|
d88605de72 | ||
|
|
8b5382dde9 | ||
|
|
72b9397f67 | ||
|
|
cda8472120 | ||
|
|
705eb7a5ea | ||
|
|
aee1e2d42b | ||
|
|
9ac073a1b4 | ||
|
|
3394ff525a | ||
|
|
05d04b50a1 | ||
|
|
42100d985b | ||
|
|
7bf3d7617e | ||
|
|
bd5735dfc9 | ||
|
|
09135b0851 | ||
|
|
055f6f3335 | ||
|
|
ddee03452c | ||
|
|
a7856ae21b | ||
|
|
ae621d929e | ||
|
|
eb30c4ac26 | ||
|
|
db47cdf706 | ||
|
|
7d7d50f99c | ||
|
|
0f16a14f7f | ||
|
|
e7f43178c0 | ||
|
|
75d6234e0c | ||
|
|
93c9400cee | ||
|
|
efb550dac6 | ||
|
|
ea11421ef3 | ||
|
|
5f38796425 | ||
|
|
71355aeede | ||
|
|
36aad5ab59 | ||
|
|
18ac5a9b55 | ||
|
|
33b587ffac | ||
|
|
66b38d5e38 | ||
|
|
79f18ad09e | ||
|
|
3204d74d25 | ||
|
|
1e45fa4d31 | ||
|
|
e55a5aea49 | ||
|
|
f3997fc8a8 | ||
|
|
1745d7c10a | ||
|
|
38c8e09913 | ||
|
|
dd8ecdd792 | ||
|
|
04b61c8b29 | ||
|
|
e9bfa96f26 | ||
|
|
95e62e6a98 | ||
|
|
c33ef49cdf | ||
|
|
cfaad60275 | ||
|
|
077484323c | ||
|
|
700fcfccf5 | ||
|
|
4b4801add6 | ||
|
|
741128cb23 | ||
|
|
3bee0f0b57 | ||
|
|
727c522fb8 | ||
|
|
00e5467e99 | ||
|
|
0371f03def | ||
|
|
d18c273293 | ||
|
|
0f24bb29ac | ||
|
|
3cb35a3a65 | ||
|
|
25b0bc72df | ||
|
|
e6fcebe4f3 | ||
|
|
46f85c0f6d | ||
|
|
7a9c3b7eb2 | ||
|
|
569a151da2 | ||
|
|
2f3e807e9d | ||
|
|
95ab37fdd2 | ||
|
|
1707dc27e3 | ||
|
|
c4126a0796 | ||
|
|
6acef3a638 | ||
|
|
b3656a0243 | ||
|
|
41b40b94fa | ||
|
|
ca59ae53a5 | ||
|
|
8c649d33ee |
@@ -69,7 +69,7 @@ OBJS := $(SRC:src/%.cpp=build/.objs/%.cpp.o)
|
||||
DEPS := $(SRC:src/%.cpp=build/.objs/%.cpp.d)
|
||||
|
||||
TESTS := $(wildcard tests/*.cpp)
|
||||
TESTS_OBJS := $(filter-out build/.objs/mergerfs.o,$(OBJS))
|
||||
TESTS_OBJS := $(filter-out build/.objs/mergerfs.cpp.o,$(OBJS))
|
||||
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.o)
|
||||
TESTS_DEPS := $(TESTS:tests/%.cpp=build/.test_objs/%.cpp.d)
|
||||
TESTS_DEPS += $(DEPS)
|
||||
@@ -115,11 +115,15 @@ LIBFUSE := libfuse/$(BUILDDIR)/libfuse.a
|
||||
LDFLAGS ?=
|
||||
LDLIBS := \
|
||||
-lrt \
|
||||
-latomic \
|
||||
-pthread \
|
||||
-lstdc++fs
|
||||
-pthread
|
||||
override LDLIBS += \
|
||||
$(LIBFUSE)
|
||||
ifeq ($(CXX),g++)
|
||||
GCC_VERSION := $(shell $(CXX) -dumpversion | cut -f1 -d.)
|
||||
ifeq ($(shell test $(GCC_VERSION) -lt 9; echo $$?),0)
|
||||
override LDLIBS += -lstdc++fs
|
||||
endif
|
||||
endif
|
||||
|
||||
# https://www.gnu.org/prep/standards/html_node/Directory-Variables.html
|
||||
DESTDIR ?=
|
||||
|
||||
@@ -25,6 +25,7 @@ etc.](https://trapexit.github.io/mergerfs/latest/project_comparisons/)
|
||||
* Works with filesystems of [almost any
|
||||
type](https://trapexit.github.io/mergerfs/latest/faq/compatibility_and_integration/#what-filesystems-can-be-used-as-branches)
|
||||
* Ignore read-only filesystems when creating files
|
||||
* [Hard links](https://trapexit.github.io/mergerfs/preview/faq/technical_behavior_and_limitations/#do-hard-links-work)
|
||||
* Hard link [copy-on-write /
|
||||
CoW](https://trapexit.github.io/mergerfs/latest/config/link_cow/)
|
||||
* [Runtime configurable](https://trapexit.github.io/mergerfs/latest/runtime_interface/)
|
||||
@@ -59,8 +60,10 @@ which branch will be selected for the creation.
|
||||
For functions which change attributes or remove the file the behavior
|
||||
may be applied to all instances found.
|
||||
|
||||
Read more about [policies
|
||||
here](https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies/).
|
||||
The way in which mergerfs behaves is controlled by the
|
||||
[config/options/settings](https://trapexit.github.io/mergerfs/latest/config/options). More
|
||||
specifically by
|
||||
[policies](https://trapexit.github.io/mergerfs/latest/config/functions_categories_policies/).
|
||||
|
||||
|
||||
### Visualization
|
||||
@@ -77,7 +80,7 @@ A + B = C
|
||||
| | |
|
||||
+-- /dir2 | +-- /dir2
|
||||
| | | | |
|
||||
| *-- file4 | | +-- file4
|
||||
| +-- file4 | | +-- file4
|
||||
| | |
|
||||
| +-- /dir3 +-- /dir3
|
||||
| | | | |
|
||||
|
||||
+13
-21
@@ -27,37 +27,29 @@ git log HEAD^1..
|
||||
mkdir "/build"
|
||||
if [ -e /usr/bin/apt-get ]; then
|
||||
make deb
|
||||
find /tmp/ \
|
||||
-type f \
|
||||
-name "*.deb" \
|
||||
-not -name "*dbgsym*" \
|
||||
-exec cp -v {} /build/ \;
|
||||
exit 0
|
||||
elif [ -e /usr/bin/dnf ]; then
|
||||
make rpm
|
||||
find /tmp/ \
|
||||
-type f \
|
||||
-name "*.rpm" \
|
||||
-not -name "*sym*" \
|
||||
-not -name "*src.rpm" \
|
||||
-exec cp -v {} /build/ \;
|
||||
exit 0
|
||||
elif [ -e /usr/bin/yum ]; then
|
||||
. /opt/rh/devtoolset-9/enable
|
||||
make rpm
|
||||
find /tmp/ \
|
||||
-type f \
|
||||
-name "*.rpm" \
|
||||
-not -name "*sym*" \
|
||||
-not -name "*src.rpm" \
|
||||
-exec cp -v {} /build/ \;
|
||||
exit 0
|
||||
elif [ -e /sbin/apk ]; then
|
||||
echo "NOT YET SUPPORTED"
|
||||
exit 1
|
||||
elif [ -e /usr/sbin/pkg ]; then
|
||||
echo "NOT YET SUPPORTED"
|
||||
exit 1
|
||||
else
|
||||
echo "NOT YET SUPPORTED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 1
|
||||
echo "Copy packages to host..."
|
||||
find "${BUILDDIR}" \
|
||||
-type f \
|
||||
\( -name "*.deb" -or -name "*.rpm" \) \
|
||||
-not -name "*dbgsym*" \
|
||||
-not -name "*sym*" \
|
||||
-not -name "*src.rpm" \
|
||||
-exec cp -v {} /build/ \;
|
||||
|
||||
exit 0
|
||||
|
||||
+1
-6
@@ -62,12 +62,7 @@ BUILDDIR := build
|
||||
SRC_C = \
|
||||
lib/buffer.c \
|
||||
lib/crc32b.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/fuse_opt.c \
|
||||
lib/fuse_session.c \
|
||||
lib/fuse_signals.c \
|
||||
lib/helper.c \
|
||||
lib/mount.c
|
||||
lib/fuse_opt.c
|
||||
SRC_CXX = $(wildcard lib/*.cpp)
|
||||
|
||||
OBJS := $(SRC_C:lib/%.c=$(BUILDDIR)/.objs/%.c.o)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
struct dirent64
|
||||
{
|
||||
public:
|
||||
uint64_t ino;
|
||||
int64_t off;
|
||||
uint16_t reclen;
|
||||
uint8_t type;
|
||||
char name[];
|
||||
|
||||
public:
|
||||
size_t
|
||||
namelen() const
|
||||
{
|
||||
return ::strlen(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
+180
-544
@@ -12,6 +12,8 @@
|
||||
#include "extern_c.h"
|
||||
#include "fuse_common.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "fuse_conn_info.hpp"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
@@ -28,12 +30,6 @@ EXTERN_C_BEGIN
|
||||
* Basic FUSE API *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/** Handle for a FUSE filesystem */
|
||||
struct fuse;
|
||||
|
||||
/** Structure containing a raw command */
|
||||
struct fuse_cmd;
|
||||
|
||||
struct fuse_dirents_t;
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
|
||||
@@ -61,516 +57,188 @@ typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
*/
|
||||
struct fuse_operations
|
||||
{
|
||||
/** Get file attributes.
|
||||
*
|
||||
* Similar to stat(). The 'st_dev' and 'st_blksize' fields are
|
||||
* ignored. The 'st_ino' field is ignored except if the 'use_ino'
|
||||
* mount option is given.
|
||||
*/
|
||||
int (*getattr) (const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Read the target of a symbolic link
|
||||
*
|
||||
* The buffer should be filled with a null terminated string. The
|
||||
* buffer size argument includes the space for the terminating
|
||||
* null character. If the linkname is too long to fit in the
|
||||
* buffer, it should be truncated. The return value should be 0
|
||||
* for success.
|
||||
*/
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
|
||||
/** Create a file node
|
||||
*
|
||||
* This is called for creation of all non-directory, non-symlink
|
||||
* nodes. If the filesystem defines a create() method, then for
|
||||
* regular files that will be called instead.
|
||||
*/
|
||||
int (*mknod) (const char *, mode_t, dev_t);
|
||||
|
||||
/** Create a directory
|
||||
*
|
||||
* Note that the mode argument may not have the type specification
|
||||
* bits set, i.e. S_ISDIR(mode) can be false. To obtain the
|
||||
* correct directory type bits use mode|S_IFDIR
|
||||
* */
|
||||
int (*mkdir) (const char *, mode_t);
|
||||
|
||||
/** Remove a file */
|
||||
int (*unlink) (const char *);
|
||||
|
||||
/** Remove a directory */
|
||||
int (*rmdir) (const char *);
|
||||
|
||||
/** Create a symbolic link */
|
||||
int (*symlink) (const char *, const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Rename a file */
|
||||
int (*rename) (const char *, const char *);
|
||||
|
||||
/** Create a hard link to a file */
|
||||
int (*link) (const char *, const char *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/** Change the permission bits of a file */
|
||||
int (*chmod) (const char *, mode_t);
|
||||
int (*fchmod)(const uint64_t, const mode_t);
|
||||
|
||||
/** Change the owner and group of a file */
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*fchown)(const uint64_t, const uid_t, const gid_t);
|
||||
|
||||
/** Change the size of a file */
|
||||
int (*truncate) (const char *, off_t);
|
||||
|
||||
/** File open operation
|
||||
*
|
||||
* No creation (O_CREAT, O_EXCL) and by default also no
|
||||
* truncation (O_TRUNC) flags will be passed to open(). If an
|
||||
* application specifies O_TRUNC, fuse first calls truncate()
|
||||
* and then open(). Only if 'atomic_o_trunc' has been
|
||||
* specified and kernel version is 2.6.24 or later, O_TRUNC is
|
||||
* passed on to open.
|
||||
*
|
||||
* Unless the 'default_permissions' mount option is given,
|
||||
* open should check if the operation is permitted for the
|
||||
* given flags. Optionally open may also return an arbitrary
|
||||
* filehandle in the fuse_file_info structure, which will be
|
||||
* passed to all file operations.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*open) (const char *, fuse_file_info_t *);
|
||||
|
||||
/** Get file system statistics
|
||||
*
|
||||
* The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored
|
||||
*
|
||||
* Replaced 'struct statfs' parameter with 'struct statvfs' in
|
||||
* version 2.5
|
||||
*/
|
||||
int (*statfs) (const char *, struct statvfs *);
|
||||
|
||||
/** Possibly flush cached data
|
||||
*
|
||||
* BIG NOTE: This is not equivalent to fsync(). It's not a
|
||||
* request to sync dirty data.
|
||||
*
|
||||
* Flush is called on each close() of a file descriptor. So if a
|
||||
* filesystem wants to return write errors in close() and the file
|
||||
* has cached dirty data, this is a good place to write back data
|
||||
* and return any errors. Since many applications ignore close()
|
||||
* errors this is not always useful.
|
||||
*
|
||||
* NOTE: The flush() method may be called more than once for each
|
||||
* open(). This happens if more than one file descriptor refers
|
||||
* to an opened file due to dup(), dup2() or fork() calls. It is
|
||||
* not possible to determine if a flush is final, so each flush
|
||||
* should be treated equally. Multiple write-flush sequences are
|
||||
* relatively rare, so this shouldn't be a problem.
|
||||
*
|
||||
* Filesystems shouldn't assume that flush will always be called
|
||||
* after some writes, or that if will be called at all.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*flush) (const fuse_file_info_t *);
|
||||
|
||||
/** Release an open file
|
||||
*
|
||||
* Release is called when there are no more references to an open
|
||||
* file: all file descriptors are closed and all memory mappings
|
||||
* are unmapped.
|
||||
*
|
||||
* For every open() call there will be exactly one release() call
|
||||
* with the same flags and file descriptor. It is possible to
|
||||
* have a file opened more than once, in which case only the last
|
||||
* release will mean, that no more reads/writes will happen on the
|
||||
* file. The return value of release is ignored.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*release) (const fuse_file_info_t *);
|
||||
|
||||
/** Synchronize file contents
|
||||
*
|
||||
* If the datasync parameter is non-zero, then only the user data
|
||||
* should be flushed, not the meta data.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*fsync) (const uint64_t, int);
|
||||
|
||||
/** Set extended attributes */
|
||||
int (*setxattr) (const char *, const char *, const char *, size_t, int);
|
||||
|
||||
/** Get extended attributes */
|
||||
int (*getxattr) (const char *, const char *, char *, size_t);
|
||||
|
||||
/** List extended attributes */
|
||||
int (*listxattr) (const char *, char *, size_t);
|
||||
|
||||
/** Remove extended attributes */
|
||||
int (*removexattr) (const char *, const char *);
|
||||
|
||||
/** Open directory
|
||||
*
|
||||
* Unless the 'default_permissions' mount option is given,
|
||||
* this method should check if opendir is permitted for this
|
||||
* directory. Optionally opendir may also return an arbitrary
|
||||
* filehandle in the fuse_file_info structure, which will be
|
||||
* passed to readdir, closedir and fsyncdir.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*opendir) (const char *,
|
||||
fuse_file_info_t *);
|
||||
|
||||
/** Read directory
|
||||
*
|
||||
* This supersedes the old getdir() interface. New applications
|
||||
* should use this.
|
||||
*
|
||||
* The filesystem may choose between two modes of operation:
|
||||
*
|
||||
* 1) The readdir implementation ignores the offset parameter, and
|
||||
* passes zero to the filler function's offset. The filler
|
||||
* function will not return '1' (unless an error happens), so the
|
||||
* whole directory is read in a single readdir operation. This
|
||||
* works just like the old getdir() method.
|
||||
*
|
||||
* 2) The readdir implementation keeps track of the offsets of the
|
||||
* directory entries. It uses the offset parameter and always
|
||||
* passes non-zero offset to the filler function. When the buffer
|
||||
* is full (or an error happens) the filler function will return
|
||||
* '1'.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*readdir)(const fuse_file_info_t *,
|
||||
int (*getattr)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
struct stat *,
|
||||
fuse_timeouts_t *);
|
||||
int (*readlink)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
char *,
|
||||
size_t);
|
||||
int (*mknod)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
mode_t,
|
||||
dev_t);
|
||||
int (*mkdir)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
mode_t);
|
||||
int (*unlink)(const fuse_req_ctx_t *,
|
||||
const char *);
|
||||
int (*rmdir)(const fuse_req_ctx_t *,
|
||||
const char *);
|
||||
int (*symlink)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const char *,
|
||||
struct stat *,
|
||||
fuse_timeouts_t *);
|
||||
int (*rename)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const char *);
|
||||
int (*link)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const char *,
|
||||
struct stat *,
|
||||
fuse_timeouts_t *);
|
||||
int (*chmod)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
mode_t);
|
||||
int (*fchmod)(const fuse_req_ctx_t *,
|
||||
const uint64_t,
|
||||
const mode_t);
|
||||
int (*chown)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
uid_t,
|
||||
gid_t);
|
||||
int (*fchown)(const fuse_req_ctx_t *,
|
||||
const uint64_t,
|
||||
const uid_t,
|
||||
const gid_t);
|
||||
int (*truncate)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
off_t);
|
||||
int (*open)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
fuse_file_info_t *);
|
||||
int (*statfs)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
struct statvfs *);
|
||||
int (*flush)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *);
|
||||
int (*release)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *);
|
||||
int (*fsync)(const fuse_req_ctx_t *,
|
||||
const uint64_t,
|
||||
int);
|
||||
int (*setxattr)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const char *,
|
||||
const char *,
|
||||
size_t,
|
||||
int);
|
||||
int (*getxattr)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const char *,
|
||||
char *,
|
||||
size_t);
|
||||
int (*listxattr)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
char *,
|
||||
size_t);
|
||||
int (*removexattr)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const char *);
|
||||
int (*opendir)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
fuse_file_info_t *);
|
||||
int (*readdir)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *,
|
||||
fuse_dirents_t *);
|
||||
|
||||
int (*readdir_plus)(const fuse_file_info_t *,
|
||||
int (*readdir_plus)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *,
|
||||
fuse_dirents_t *);
|
||||
|
||||
|
||||
/** Release directory
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*releasedir) (const fuse_file_info_t *);
|
||||
|
||||
/** Synchronize directory contents
|
||||
*
|
||||
* If the datasync parameter is non-zero, then only the user data
|
||||
* should be flushed, not the meta data
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*fsyncdir) (const fuse_file_info_t *, int);
|
||||
|
||||
/**
|
||||
* Initialize filesystem
|
||||
*
|
||||
* The return value will passed in the private_data field of
|
||||
* fuse_context to all file operations and as a parameter to the
|
||||
* destroy() method.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
* Changed in version 2.6
|
||||
*/
|
||||
void *(*init) (struct fuse_conn_info *conn);
|
||||
|
||||
/**
|
||||
* Clean up filesystem
|
||||
*
|
||||
* Called on filesystem exit.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
void (*destroy) (void);
|
||||
|
||||
/**
|
||||
* Check file access permissions
|
||||
*
|
||||
* This will be called for the access() system call. If the
|
||||
* 'default_permissions' mount option is given, this method is not
|
||||
* called.
|
||||
*
|
||||
* This method is not called under Linux kernel versions 2.4.x
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*access) (const char *, int);
|
||||
|
||||
/**
|
||||
* Create and open a file
|
||||
*
|
||||
* If the file does not exist, first create it with the specified
|
||||
* mode, and then open it.
|
||||
*
|
||||
* If this method is not implemented or under Linux kernel
|
||||
* versions earlier than 2.6.15, the mknod() and open() methods
|
||||
* will be called instead.
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*create) (const char *, mode_t, fuse_file_info_t *);
|
||||
|
||||
/**
|
||||
* Change the size of an open file
|
||||
*
|
||||
* This method is called instead of the truncate() method if the
|
||||
* truncation was invoked from an ftruncate() system call.
|
||||
*
|
||||
* If this method is not implemented or under Linux kernel
|
||||
* versions earlier than 2.6.15, the truncate() method will be
|
||||
* called instead.
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*ftruncate) (const uint64_t, off_t);
|
||||
|
||||
/**
|
||||
* Get attributes from an open file
|
||||
*
|
||||
* This method is called instead of the getattr() method if the
|
||||
* file information is available.
|
||||
*
|
||||
* Currently this is only called after the create() method if that
|
||||
* is implemented (see above). Later it may be called for
|
||||
* invocations of fstat() too.
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*fgetattr) (const uint64_t, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/**
|
||||
* Perform POSIX file locking operation
|
||||
*
|
||||
* The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW.
|
||||
*
|
||||
* For the meaning of fields in 'struct flock' see the man page
|
||||
* for fcntl(2). The l_whence field will always be set to
|
||||
* SEEK_SET.
|
||||
*
|
||||
* For checking lock ownership, the 'fuse_file_info->owner'
|
||||
* argument must be used.
|
||||
*
|
||||
* For F_GETLK operation, the library will first check currently
|
||||
* held locks, and if a conflicting lock is found it will return
|
||||
* information without calling this method. This ensures, that
|
||||
* for local locks the l_pid field is correctly filled in. The
|
||||
* results may not be accurate in case of race conditions and in
|
||||
* the presence of hard links, but it's unlikely that an
|
||||
* application would rely on accurate GETLK results in these
|
||||
* cases. If a conflicting lock is not found, this method will be
|
||||
* called, and the filesystem may fill out l_pid by a meaningful
|
||||
* value, or it may leave this field zero.
|
||||
*
|
||||
* For F_SETLK and F_SETLKW the l_pid field will be set to the pid
|
||||
* of the process performing the locking operation.
|
||||
*
|
||||
* Note: if this method is not implemented, the kernel will still
|
||||
* allow file locking to work locally. Hence it is only
|
||||
* interesting for network filesystems and similar.
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*lock) (const fuse_file_info_t *,
|
||||
int cmd,
|
||||
struct flock *);
|
||||
|
||||
/**
|
||||
* Change the access and modification times of a file with
|
||||
* nanosecond resolution
|
||||
*
|
||||
* This supersedes the old utime() interface. New applications
|
||||
* should use this.
|
||||
*
|
||||
* See the utimensat(2) man page for details.
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*utimens)(const char *, const struct timespec tv[2]);
|
||||
int (*futimens)(const uint64_t fh, const struct timespec tv[2]);
|
||||
|
||||
/**
|
||||
* Map block index within file to block index within device
|
||||
*
|
||||
* Note: This makes sense only for block device backed filesystems
|
||||
* mounted with the 'blkdev' option
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
|
||||
|
||||
/**
|
||||
* Ioctl
|
||||
*
|
||||
* flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
|
||||
* 64bit environment. The size and direction of data is
|
||||
* determined by _IOC_*() decoding of cmd. For _IOC_NONE,
|
||||
* data will be NULL, for _IOC_WRITE data is out area, for
|
||||
* _IOC_READ in area and if both are set in/out area. In all
|
||||
* non-NULL cases, the area is of _IOC_SIZE(cmd) bytes.
|
||||
*
|
||||
* If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a
|
||||
* directory file handle.
|
||||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*ioctl) (const fuse_file_info_t *ffi,
|
||||
unsigned long cmd,
|
||||
void *arg,
|
||||
unsigned int flags,
|
||||
void *data,
|
||||
uint32_t *out_bufsz);
|
||||
|
||||
/**
|
||||
* Poll for IO readiness events
|
||||
*
|
||||
* Note: If ph is non-NULL, the client should notify
|
||||
* when IO readiness events occur by calling
|
||||
* fuse_notify_poll() with the specified ph.
|
||||
*
|
||||
* Regardless of the number of times poll with a non-NULL ph
|
||||
* is received, single notification is enough to clear all.
|
||||
* Notifying more times incurs overhead but doesn't harm
|
||||
* correctness.
|
||||
*
|
||||
* The callee is responsible for destroying ph with
|
||||
* fuse_pollhandle_destroy() when no longer in use.
|
||||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*poll) (const fuse_file_info_t *ffi,
|
||||
fuse_pollhandle_t *ph,
|
||||
unsigned *reventsp);
|
||||
|
||||
/** Write contents of buffer to an open file
|
||||
*
|
||||
* Similar to the write() method, but data is supplied in a
|
||||
* generic buffer. Use fuse_buf_copy() to transfer data to
|
||||
* the destination.
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*write) (const fuse_file_info_t *ffi,
|
||||
const char *data,
|
||||
size_t size,
|
||||
off_t off);
|
||||
|
||||
/** Store data from an open file in a buffer
|
||||
*
|
||||
* Similar to the read() method, but data is stored and
|
||||
* returned in a generic buffer.
|
||||
*
|
||||
* No actual copying of data has to take place, the source
|
||||
* file descriptor may simply be stored in the buffer for
|
||||
* later data transfer.
|
||||
*
|
||||
* The buffer must be allocated dynamically and stored at the
|
||||
* location pointed to by bufp. If the buffer contains memory
|
||||
* regions, they too must be allocated using malloc(). The
|
||||
* allocated memory will be freed by the caller.
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*read)(const fuse_file_info_t *ffi,
|
||||
int (*releasedir)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *);
|
||||
int (*fsyncdir)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *,
|
||||
int);
|
||||
void *(*init)(struct fuse_conn_info_t *conn);
|
||||
void (*destroy)(void);
|
||||
int (*access)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
int);
|
||||
int (*create)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
mode_t,
|
||||
fuse_file_info_t *);
|
||||
int (*ftruncate)(const fuse_req_ctx_t *,
|
||||
const uint64_t,
|
||||
off_t);
|
||||
int (*fgetattr)(const fuse_req_ctx_t *,
|
||||
const uint64_t,
|
||||
struct stat *,
|
||||
fuse_timeouts_t *);
|
||||
int (*lock)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *,
|
||||
int cmd,
|
||||
struct flock *);
|
||||
int (*utimens)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
const struct timespec tv[2]);
|
||||
int (*futimens)(const fuse_req_ctx_t *,
|
||||
const uint64_t fh,
|
||||
const struct timespec tv[2]);
|
||||
int (*bmap)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
size_t blocksize,
|
||||
uint64_t *idx);
|
||||
int (*ioctl)(const fuse_req_ctx_t *ctx,
|
||||
const fuse_file_info_t *ffi,
|
||||
unsigned long cmd,
|
||||
void *arg,
|
||||
unsigned int flags,
|
||||
void *data,
|
||||
uint32_t *out_bufsz);
|
||||
int (*write)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *ffi,
|
||||
const char *data,
|
||||
size_t size,
|
||||
off_t off);
|
||||
int (*read)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *ffi,
|
||||
char *buf,
|
||||
size_t size,
|
||||
off_t off);
|
||||
/**
|
||||
* Perform BSD file locking operation
|
||||
*
|
||||
* The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN
|
||||
*
|
||||
* Nonblocking requests will be indicated by ORing LOCK_NB to
|
||||
* the above operations
|
||||
*
|
||||
* For more information see the flock(2) manual page.
|
||||
*
|
||||
* Additionally fi->owner will be set to a value unique to
|
||||
* this open file. This same value will be supplied to
|
||||
* ->release() when the file is released.
|
||||
*
|
||||
* Note: if this method is not implemented, the kernel will still
|
||||
* allow file locking to work locally. Hence it is only
|
||||
* interesting for network filesystems and similar.
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*flock) (const fuse_file_info_t *, int op);
|
||||
|
||||
/**
|
||||
* Allocates space for an open file
|
||||
*
|
||||
* This function ensures that required space is allocated for specified
|
||||
* file. If this function returns success then any subsequent write
|
||||
* request to specified range is guaranteed not to fail because of lack
|
||||
* of space on the file system media.
|
||||
*
|
||||
* Introduced in version 2.9.1
|
||||
*/
|
||||
int (*fallocate) (const uint64_t, int, off_t, off_t);
|
||||
|
||||
/**
|
||||
* Copy a range of data from one file to another
|
||||
*
|
||||
* Performs an optimized copy between two file descriptors without
|
||||
* the additional cost of transferring data through the FUSE kernel
|
||||
* module to user space (glibc) and then back into the FUSE filesystem
|
||||
* again.
|
||||
*
|
||||
* In case this method is not implemented, glibc falls back to
|
||||
* reading data from the source and writing to the
|
||||
* destination. Effectively doing an inefficient copy of the
|
||||
* data.
|
||||
*/
|
||||
ssize_t (*copy_file_range)(const fuse_file_info_t *fi_in,
|
||||
off_t offset_in,
|
||||
const fuse_file_info_t *fi_out,
|
||||
off_t offset_out,
|
||||
size_t size,
|
||||
int flags);
|
||||
|
||||
ssize_t (*setupmapping)(uint64_t *fh_,
|
||||
int (*flock)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *,
|
||||
int op);
|
||||
int (*fallocate)(const fuse_req_ctx_t *,
|
||||
const uint64_t,
|
||||
int,
|
||||
off_t,
|
||||
off_t);
|
||||
ssize_t (*copy_file_range)(const fuse_req_ctx_t *,
|
||||
const fuse_file_info_t *src_fi,
|
||||
off_t src_off,
|
||||
const fuse_file_info_t *dst_fi,
|
||||
off_t dst_off,
|
||||
const size_t size,
|
||||
const unsigned int flags);
|
||||
ssize_t (*setupmapping)(const fuse_req_ctx_t *,
|
||||
uint64_t *fh_,
|
||||
uint64_t foffset_,
|
||||
uint64_t len_,
|
||||
uint64_t flags_,
|
||||
uint64_t moffset_);
|
||||
|
||||
int (*removemapping)();
|
||||
int (*syncfs)();
|
||||
int (*tmpfile)(const char *, mode_t, fuse_file_info_t *);
|
||||
int (*statx)(const char *fusepath,
|
||||
int (*removemapping)(const fuse_req_ctx_t *);
|
||||
int (*syncfs)(const fuse_req_ctx_t *);
|
||||
int (*tmpfile)(const fuse_req_ctx_t *,
|
||||
const char *,
|
||||
mode_t,
|
||||
fuse_file_info_t *);
|
||||
int (*statx)(const fuse_req_ctx_t *,
|
||||
const char *fusepath,
|
||||
const uint32_t flags,
|
||||
const uint32_t mask,
|
||||
struct fuse_statx *st,
|
||||
fuse_timeouts_t *timeout);
|
||||
int (*statx_fh)(const uint64_t fh,
|
||||
int (*statx_fh)(const fuse_req_ctx_t *,
|
||||
const uint64_t fh,
|
||||
const uint32_t flags,
|
||||
const uint32_t mask,
|
||||
struct fuse_statx *st,
|
||||
fuse_timeouts_t *timeout);
|
||||
};
|
||||
|
||||
/** Extra context that may be needed by some filesystems
|
||||
*
|
||||
* The uid, gid and pid fields are not filled in case of a writepage
|
||||
* operation.
|
||||
*/
|
||||
struct fuse_context
|
||||
{
|
||||
uint64_t unique;
|
||||
uint64_t nodeid;
|
||||
uint32_t opcode;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
mode_t umask;
|
||||
struct fuse *fuse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main function of FUSE.
|
||||
*
|
||||
@@ -593,11 +261,6 @@ struct fuse_context
|
||||
* @param op the file system operation
|
||||
* @return 0 on success, nonzero on failure
|
||||
*/
|
||||
/*
|
||||
int fuse_main(int argc, char *argv[], const struct fuse_operations *op);
|
||||
*/
|
||||
#define fuse_main(argc, argv, op) \
|
||||
fuse_main_real(argc, argv, op, sizeof(*(op)))
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* More detailed API *
|
||||
@@ -612,8 +275,9 @@ struct fuse_context
|
||||
* @param op_size the size of the fuse_operations structure
|
||||
* @return the created FUSE handle
|
||||
*/
|
||||
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
|
||||
const struct fuse_operations *op, size_t op_size);
|
||||
struct fuse *fuse_new(struct fuse_chan *ch,
|
||||
struct fuse_args *args,
|
||||
const struct fuse_operations *op);
|
||||
|
||||
/**
|
||||
* Destroy the FUSE handle.
|
||||
@@ -625,7 +289,7 @@ struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,
|
||||
*
|
||||
* @param f the FUSE handle
|
||||
*/
|
||||
void fuse_destroy(struct fuse *f);
|
||||
void fuse_destroy();
|
||||
|
||||
/**
|
||||
* Exit from event loop
|
||||
@@ -686,7 +350,9 @@ int fuse_is_lib_option(const char *opt);
|
||||
*
|
||||
* Do not call this directly, use fuse_main()
|
||||
*/
|
||||
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size);
|
||||
int fuse_main(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op);
|
||||
|
||||
void fuse_populate_maintenance_thread(struct fuse *fuse);
|
||||
|
||||
@@ -709,13 +375,6 @@ int fuse_clean_cache(struct fuse *fuse);
|
||||
* Stacking API
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fuse filesystem object
|
||||
*
|
||||
* This is opaque object represents a filesystem layer
|
||||
*/
|
||||
struct fuse_fs;
|
||||
|
||||
/*
|
||||
* These functions call the relevant filesystem operation, and return
|
||||
* the result.
|
||||
@@ -725,20 +384,6 @@ struct fuse_fs;
|
||||
* fuse_fs_releasedir and fuse_fs_statfs, which return 0.
|
||||
*/
|
||||
|
||||
int fuse_notify_poll(fuse_pollhandle_t *ph);
|
||||
|
||||
/**
|
||||
* Create a new fuse filesystem object
|
||||
*
|
||||
* This is usually called from the factory of a fuse module to create
|
||||
* a new instance of a filesystem.
|
||||
*
|
||||
* @param op the filesystem operations
|
||||
* @param op_size the size of the fuse_operations structure
|
||||
* @return a new filesystem object
|
||||
*/
|
||||
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Advanced API for event handling, don't worry about this... *
|
||||
* ----------------------------------------------------------- */
|
||||
@@ -746,40 +391,31 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size);
|
||||
/* NOTE: the following functions are deprecated, and will be removed
|
||||
from the 3.0 API. Use the lowlevel session functions instead */
|
||||
|
||||
/** Function type used to process commands */
|
||||
typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
|
||||
|
||||
/** This is the part of fuse_main() before the event loop */
|
||||
struct fuse *fuse_setup(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
struct fuse *fuse_setup(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
char **mountpoint);
|
||||
|
||||
/** This is the part of fuse_main() after the event loop */
|
||||
void fuse_teardown(struct fuse *fuse, char *mountpoint);
|
||||
|
||||
/** Multi threaded event loop, which calls the custom command
|
||||
processor function */
|
||||
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data);
|
||||
void fuse_teardown(char *mountpoint);
|
||||
|
||||
/** Return the exited flag, which indicates if fuse_exit() has been
|
||||
called */
|
||||
int fuse_exited(struct fuse *f);
|
||||
int fuse_exited();
|
||||
|
||||
/** This function is obsolete and implemented as a no-op */
|
||||
void fuse_set_getcontext_func(struct fuse_context *(*func)(void));
|
||||
|
||||
/** Get session from fuse object */
|
||||
struct fuse_session *fuse_get_session(struct fuse *f);
|
||||
struct fuse_session *fuse_get_session();
|
||||
|
||||
void fuse_gc1();
|
||||
void fuse_gc();
|
||||
void fuse_invalidate_all_nodes();
|
||||
|
||||
int fuse_get_dev_fuse_fd(const struct fuse_context *fc);
|
||||
int fuse_passthrough_open(const struct fuse_context *fc,
|
||||
const int fd);
|
||||
int fuse_passthrough_close(const struct fuse_context *fc,
|
||||
const int backing_id);
|
||||
int fuse_passthrough_open(const int fd);
|
||||
int fuse_passthrough_close(const int backing_id);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#define FUSE_CFG_INVALID_ID -1
|
||||
#define FUSE_CFG_INVALID_UMASK -1
|
||||
|
||||
|
||||
struct fuse_cfg_t
|
||||
{
|
||||
s64 uid = FUSE_CFG_INVALID_ID;
|
||||
bool valid_uid() const;
|
||||
|
||||
s64 gid = FUSE_CFG_INVALID_ID;
|
||||
bool valid_gid() const;
|
||||
|
||||
s64 umask = FUSE_CFG_INVALID_UMASK;
|
||||
bool valid_umask() const;
|
||||
|
||||
s64 remember_nodes = 0;
|
||||
|
||||
bool debug = false;
|
||||
|
||||
int max_background = 0;
|
||||
int congestion_threshold = 0;
|
||||
u32 max_pages = 32;
|
||||
int passthrough_max_stack_depth = 1;
|
||||
|
||||
int read_thread_count = 0;
|
||||
int process_thread_count = -1;
|
||||
int process_thread_queue_depth = 2;
|
||||
std::string pin_threads = "false";
|
||||
};
|
||||
|
||||
extern fuse_cfg_t fuse_cfg;
|
||||
@@ -1,19 +1,4 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
/** @file */
|
||||
|
||||
#if !defined(_FUSE_H_) && !defined(_FUSE_LOWLEVEL_H_)
|
||||
#error "Never include <fuse_common.h> directly; use <fuse.h> or <fuse_lowlevel.h> instead."
|
||||
#endif
|
||||
|
||||
#ifndef _FUSE_COMMON_H_
|
||||
#define _FUSE_COMMON_H_
|
||||
#pragma once
|
||||
|
||||
#include "extern_c.h"
|
||||
#include "fuse_opt.h"
|
||||
@@ -22,15 +7,6 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/** Major version of FUSE library interface */
|
||||
#define FUSE_MAJOR_VERSION 2
|
||||
|
||||
/** Minor version of FUSE library interface */
|
||||
#define FUSE_MINOR_VERSION 9
|
||||
|
||||
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
|
||||
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
|
||||
|
||||
/* This interface uses 64 bit off_t */
|
||||
#if _FILE_OFFSET_BITS != 64
|
||||
#error Please add -D_FILE_OFFSET_BITS=64 to your compile flags!
|
||||
@@ -133,6 +109,7 @@ struct fuse_file_info_t
|
||||
#define FUSE_CAP_PASSTHROUGH (1ULL << 25)
|
||||
#define FUSE_CAP_HANDLE_KILLPRIV (1ULL << 26)
|
||||
#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1ULL << 27)
|
||||
#define FUSE_CAP_ALLOW_IDMAP (1ULL << 28)
|
||||
|
||||
/**
|
||||
* Ioctl flags
|
||||
@@ -151,30 +128,8 @@ struct fuse_file_info_t
|
||||
|
||||
#define FUSE_IOCTL_MAX_IOV 256
|
||||
|
||||
/**
|
||||
* Connection information, passed to the ->init() method
|
||||
*
|
||||
* Some of the elements are read-write, these can be changed to
|
||||
* indicate the value requested by the filesystem. The requested
|
||||
* value must usually be smaller than the indicated value.
|
||||
*/
|
||||
struct fuse_conn_info
|
||||
{
|
||||
unsigned proto_major;
|
||||
unsigned proto_minor;
|
||||
unsigned max_write;
|
||||
unsigned max_readahead;
|
||||
uint64_t capable;
|
||||
uint64_t want;
|
||||
unsigned max_background;
|
||||
unsigned congestion_threshold;
|
||||
uint16_t max_pages;
|
||||
};
|
||||
|
||||
struct fuse_session;
|
||||
struct fuse_chan;
|
||||
struct fuse_pollhandle_t;
|
||||
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
|
||||
|
||||
/**
|
||||
* Create a FUSE mountpoint
|
||||
@@ -235,13 +190,6 @@ int fuse_daemonize(int foreground);
|
||||
*/
|
||||
int fuse_version(void);
|
||||
|
||||
/**
|
||||
* Destroy poll handle
|
||||
*
|
||||
* @param ph the poll handle
|
||||
*/
|
||||
void fuse_pollhandle_destroy(fuse_pollhandle_t *ph);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Data buffer *
|
||||
* ----------------------------------------------------------- */
|
||||
@@ -448,5 +396,3 @@ int fuse_set_signal_handlers(struct fuse_session *se);
|
||||
void fuse_remove_signal_handlers(struct fuse_session *se);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif /* _FUSE_COMMON_H_ */
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
int fuse_config_get_read_thread_count();
|
||||
int fuse_config_get_process_thread_count();
|
||||
int fuse_config_get_process_thread_queue_depth();
|
||||
std::string fuse_config_get_pin_threads();
|
||||
|
||||
void fuse_config_set_read_thread_count(int const);
|
||||
void fuse_config_set_process_thread_count(int const);
|
||||
void fuse_config_set_process_thread_queue_depth(int const);
|
||||
void fuse_config_set_pin_threads(std::string const);
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
|
||||
typedef struct fuse_conn_info_t fuse_conn_info_t;
|
||||
struct fuse_conn_info_t
|
||||
{
|
||||
u32 proto_major;
|
||||
u32 proto_minor;
|
||||
u64 capable;
|
||||
u64 want;
|
||||
};
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#include "kvec.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "linux_dirent64.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum fuse_dirents_type_e
|
||||
{
|
||||
UNSET = 0,
|
||||
NORMAL,
|
||||
PLUS
|
||||
};
|
||||
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
|
||||
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
struct fuse_dirents_t
|
||||
{
|
||||
kvec_t(char) data;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
};
|
||||
|
||||
int fuse_dirents_init(fuse_dirents_t *d);
|
||||
void fuse_dirents_free(fuse_dirents_t *d);
|
||||
void fuse_dirents_reset(fuse_dirents_t *d);
|
||||
|
||||
int fuse_dirents_add(fuse_dirents_t *d,
|
||||
const struct dirent *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add_plus(fuse_dirents_t *d,
|
||||
const struct dirent *de,
|
||||
const uint64_t namelen,
|
||||
const fuse_entry_t *entry,
|
||||
const struct stat *st);
|
||||
int fuse_dirents_add_linux(fuse_dirents_t *d,
|
||||
const linux_dirent64_t *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
|
||||
const linux_dirent64_t *de,
|
||||
const uint64_t namelen,
|
||||
const fuse_entry_t *entry,
|
||||
const struct stat *st);
|
||||
|
||||
void *fuse_dirents_find(fuse_dirents_t *d,
|
||||
const uint64_t ino);
|
||||
|
||||
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kvec.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "fs_dirent64.hpp"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct fuse_dirents_t
|
||||
{
|
||||
kvec_t(char) data;
|
||||
kvec_t(uint32_t) offs;
|
||||
};
|
||||
|
||||
int fuse_dirents_init(fuse_dirents_t *d);
|
||||
void fuse_dirents_free(fuse_dirents_t *d);
|
||||
void fuse_dirents_reset(fuse_dirents_t *d);
|
||||
|
||||
int fuse_dirents_add(fuse_dirents_t *d,
|
||||
const dirent *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add(fuse_dirents_t *d,
|
||||
const fs::dirent64 *de,
|
||||
const uint64_t namelen);
|
||||
@@ -1,17 +1,9 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_LOWLEVEL_H_
|
||||
#define _FUSE_LOWLEVEL_H_
|
||||
#pragma once
|
||||
|
||||
#include "extern_c.h"
|
||||
#include "fuse_common.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_req.hpp"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
@@ -30,9 +22,6 @@ EXTERN_C_BEGIN
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
|
||||
/** Request pointer type */
|
||||
typedef struct fuse_req *fuse_req_t;
|
||||
|
||||
/**
|
||||
* Session
|
||||
*
|
||||
@@ -88,18 +77,6 @@ struct fuse_entry_param
|
||||
fuse_timeouts_t timeout;
|
||||
};
|
||||
|
||||
/** Additional context associated with requests */
|
||||
struct fuse_ctx
|
||||
{
|
||||
uint64_t unique;
|
||||
uint64_t nodeid;
|
||||
uint32_t opcode;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
mode_t umask;
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Request methods and replies *
|
||||
* ----------------------------------------------------------- */
|
||||
@@ -127,55 +104,55 @@ struct fuse_ctx
|
||||
*/
|
||||
struct fuse_lowlevel_ops
|
||||
{
|
||||
void (*access)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*bmap)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*copy_file_range)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*create)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*access)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*bmap)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*copy_file_range)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*create)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*destroy)(void *userdata);
|
||||
void (*fallocate)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*flock)(fuse_req_t req, uint64_t ino, fuse_file_info_t *fi, int op);
|
||||
void (*flush)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*forget)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*forget_multi)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*fsync)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*fsyncdir)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*getattr)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*getlk)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*getxattr)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*init)(void *userdata, struct fuse_conn_info *conn);
|
||||
void (*ioctl)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*link)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*listxattr)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*lookup)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*lseek)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*mkdir)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*mknod)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*open)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*opendir)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*poll)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*read)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*readdir)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*readdir_plus)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*readlink)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*release)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*releasedir)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*removemapping)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*removexattr)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*rename)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*rename2)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*retrieve_reply)(fuse_req_t req, void *cookie, uint64_t ino, off_t offset);
|
||||
void (*rmdir)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*setattr)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*setlk)(fuse_req_t req, uint64_t ino, fuse_file_info_t *fi, struct flock *lock, int sleep);
|
||||
void (*setupmapping)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*setxattr)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*statfs)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*statx)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*symlink)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*syncfs)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*tmpfile)(fuse_req_t req, const struct fuse_in_header *hdr);
|
||||
void (*unlink)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*write)(fuse_req_t req, struct fuse_in_header *hdr);
|
||||
void (*fallocate)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*flock)(fuse_req_t *req, uint64_t ino, fuse_file_info_t *fi, int op);
|
||||
void (*flush)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*forget)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*forget_multi)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*fsync)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*fsyncdir)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*getattr)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*getlk)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*getxattr)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*init)(void *userdata, fuse_conn_info_t *conn);
|
||||
void (*ioctl)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*link)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*listxattr)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*lookup)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*lseek)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*mkdir)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*mknod)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*open)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*opendir)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*poll)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*read)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*readdir)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*readdir_plus)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*readlink)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*release)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*releasedir)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*removemapping)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*removexattr)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*rename)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*rename2)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*retrieve_reply)(fuse_req_t *req, void *cookie, uint64_t ino, off_t offset);
|
||||
void (*rmdir)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*setattr)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*setlk)(fuse_req_t *req, uint64_t ino, fuse_file_info_t *fi, struct flock *lock, int sleep);
|
||||
void (*setupmapping)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*setxattr)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*statfs)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*statx)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*symlink)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*syncfs)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*tmpfile)(fuse_req_t *req, const struct fuse_in_header *hdr);
|
||||
void (*unlink)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
void (*write)(fuse_req_t *req, struct fuse_in_header *hdr);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -191,7 +168,7 @@ struct fuse_lowlevel_ops
|
||||
* @param err the positive error value, or zero for success
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_err(fuse_req_t req, int err);
|
||||
int fuse_reply_err(fuse_req_t *req, int err);
|
||||
|
||||
/**
|
||||
* Don't send reply
|
||||
@@ -201,7 +178,7 @@ int fuse_reply_err(fuse_req_t req, int err);
|
||||
*
|
||||
* @param req request handle
|
||||
*/
|
||||
void fuse_reply_none(fuse_req_t req);
|
||||
void fuse_reply_none(fuse_req_t *req);
|
||||
|
||||
/**
|
||||
* Reply with a directory entry
|
||||
@@ -216,7 +193,7 @@ void fuse_reply_none(fuse_req_t req);
|
||||
* @param e the entry parameters
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
|
||||
int fuse_reply_entry(fuse_req_t *req, const struct fuse_entry_param *e);
|
||||
|
||||
/**
|
||||
* Reply with a directory entry and open parameters
|
||||
@@ -235,7 +212,8 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
|
||||
* @param fi file information
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
||||
int fuse_reply_create(fuse_req_t *req,
|
||||
const struct fuse_entry_param *e,
|
||||
const fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
@@ -249,12 +227,12 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
||||
* @param attr_timeout validity timeout (in seconds) for the attributes
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_attr(fuse_req_t req,
|
||||
int fuse_reply_attr(fuse_req_t *req,
|
||||
const struct stat *attr,
|
||||
const uint64_t timeout);
|
||||
|
||||
|
||||
int fuse_reply_statx(fuse_req_t req,
|
||||
int fuse_reply_statx(fuse_req_t *req,
|
||||
int flags,
|
||||
struct fuse_statx *st,
|
||||
const uint64_t timeout);
|
||||
@@ -269,7 +247,7 @@ int fuse_reply_statx(fuse_req_t req,
|
||||
* @param link symbolic link contents
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_readlink(fuse_req_t req, const char *link);
|
||||
int fuse_reply_readlink(fuse_req_t *req, const char *link);
|
||||
|
||||
/**
|
||||
* Reply with open parameters
|
||||
@@ -284,7 +262,7 @@ int fuse_reply_readlink(fuse_req_t req, const char *link);
|
||||
* @param fi file information
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_open(fuse_req_t req,
|
||||
int fuse_reply_open(fuse_req_t *req,
|
||||
const fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
@@ -297,7 +275,7 @@ int fuse_reply_open(fuse_req_t req,
|
||||
* @param count the number of bytes written
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_write(fuse_req_t req, size_t count);
|
||||
int fuse_reply_write(fuse_req_t *req, size_t count);
|
||||
|
||||
/**
|
||||
* Reply with data
|
||||
@@ -310,9 +288,9 @@ int fuse_reply_write(fuse_req_t req, size_t count);
|
||||
* @param size the size of data in bytes
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size);
|
||||
int fuse_reply_buf(fuse_req_t *req, const char *buf, size_t size);
|
||||
|
||||
int fuse_reply_data(fuse_req_t req,
|
||||
int fuse_reply_data(fuse_req_t *req,
|
||||
char *buf,
|
||||
size_t bufsize);
|
||||
|
||||
@@ -327,7 +305,7 @@ int fuse_reply_data(fuse_req_t req,
|
||||
* @param count the size of vector
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
|
||||
int fuse_reply_iov(fuse_req_t *req, const struct iovec *iov, int count);
|
||||
|
||||
/**
|
||||
* Reply with filesystem statistics
|
||||
@@ -339,7 +317,7 @@ int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count);
|
||||
* @param stbuf filesystem statistics
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
|
||||
int fuse_reply_statfs(fuse_req_t *req, const struct statvfs *stbuf);
|
||||
|
||||
/**
|
||||
* Reply with needed buffer size
|
||||
@@ -351,7 +329,7 @@ int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf);
|
||||
* @param count the buffer size needed in bytes
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_xattr(fuse_req_t req, size_t count);
|
||||
int fuse_reply_xattr(fuse_req_t *req, size_t count);
|
||||
|
||||
/**
|
||||
* Reply with file lock information
|
||||
@@ -363,7 +341,7 @@ int fuse_reply_xattr(fuse_req_t req, size_t count);
|
||||
* @param lock the lock information
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
|
||||
int fuse_reply_lock(fuse_req_t *req, const struct flock *lock);
|
||||
|
||||
/**
|
||||
* Reply with block index
|
||||
@@ -375,7 +353,7 @@ int fuse_reply_lock(fuse_req_t req, const struct flock *lock);
|
||||
* @param idx block index within device
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
|
||||
int fuse_reply_bmap(fuse_req_t *req, uint64_t idx);
|
||||
|
||||
/**
|
||||
* Reply to ask for data fetch and output buffer preparation. ioctl
|
||||
@@ -392,7 +370,7 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx);
|
||||
* @param out_count number of entries in out_iov
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_ioctl_retry(fuse_req_t req,
|
||||
int fuse_reply_ioctl_retry(fuse_req_t *req,
|
||||
const struct iovec *in_iov, size_t in_count,
|
||||
const struct iovec *out_iov, size_t out_count);
|
||||
|
||||
@@ -407,7 +385,7 @@ int fuse_reply_ioctl_retry(fuse_req_t req,
|
||||
* @param buf buffer containing output data
|
||||
* @param size length of output data
|
||||
*/
|
||||
int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, uint32_t size);
|
||||
int fuse_reply_ioctl(fuse_req_t *req, int result, const void *buf, uint32_t size);
|
||||
|
||||
/**
|
||||
* Reply to finish ioctl with iov buffer
|
||||
@@ -420,30 +398,13 @@ int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, uint32_t size)
|
||||
* @param iov the vector containing the data
|
||||
* @param count the size of vector
|
||||
*/
|
||||
int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov,
|
||||
int fuse_reply_ioctl_iov(fuse_req_t *req, int result, const struct iovec *iov,
|
||||
int count);
|
||||
|
||||
/**
|
||||
* Reply with poll result event mask
|
||||
*
|
||||
* @param req request handle
|
||||
* @param revents poll result event mask
|
||||
*/
|
||||
int fuse_reply_poll(fuse_req_t req, unsigned revents);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Notification *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Notify IO readiness event
|
||||
*
|
||||
* For more information, please read comment for poll operation.
|
||||
*
|
||||
* @param ph poll handle to notify IO readiness event for
|
||||
*/
|
||||
int fuse_lowlevel_notify_poll(fuse_pollhandle_t *ph);
|
||||
|
||||
/**
|
||||
* Notify to invalidate cache for an inode
|
||||
*
|
||||
@@ -551,25 +512,6 @@ int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, uint64_t ino,
|
||||
* Utility functions *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the userdata from the request
|
||||
*
|
||||
* @param req request handle
|
||||
* @return the user data passed to fuse_lowlevel_new()
|
||||
*/
|
||||
void *fuse_req_userdata(fuse_req_t req);
|
||||
|
||||
/**
|
||||
* Get the context from the request
|
||||
*
|
||||
* The pointer returned by this function will only be valid for the
|
||||
* request's lifetime
|
||||
*
|
||||
* @param req request handle
|
||||
* @return the context structure
|
||||
*/
|
||||
const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
|
||||
|
||||
/**
|
||||
* Get the current supplementary group IDs for the specified request
|
||||
*
|
||||
@@ -589,7 +531,7 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
|
||||
* @param list array of group IDs to be filled in
|
||||
* @return the total number of supplementary group IDs or -errno on failure
|
||||
*/
|
||||
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
|
||||
int fuse_req_getgroups(fuse_req_t *req, int size, gid_t list[]);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Filesystem setup *
|
||||
@@ -615,8 +557,7 @@ struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
|
||||
* Session interface *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
struct fuse_session *fuse_session_new(void *data,
|
||||
void *receive_buf,
|
||||
struct fuse_session *fuse_session_new(void *receive_buf,
|
||||
void *process_buf,
|
||||
void *destroy);
|
||||
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch);
|
||||
@@ -679,5 +620,3 @@ struct fuse_session *fuse_chan_session(struct fuse_chan *ch);
|
||||
void fuse_chan_destroy(struct fuse_chan *ch);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif /* _FUSE_LOWLEVEL_H_ */
|
||||
@@ -18,11 +18,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_msgbuf.h"
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
#include "int_types.h"
|
||||
#include "fuse_msgbuf_t.h"
|
||||
|
||||
u32 msgbuf_get_pagesize();
|
||||
void msgbuf_set_bufsize(const uint32_t size);
|
||||
uint64_t msgbuf_get_bufsize();
|
||||
|
||||
@@ -35,8 +34,3 @@ void msgbuf_gc_10percent();
|
||||
|
||||
uint64_t msgbuf_alloc_count();
|
||||
uint64_t msgbuf_avail_count();
|
||||
|
||||
void msgbuf_page_align(fuse_msgbuf_t *msgbuf);
|
||||
void msgbuf_write_align(fuse_msgbuf_t *msgbuf);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct fuse_chan;
|
||||
struct fuse_ll;
|
||||
|
||||
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
|
||||
struct fuse_pollhandle_t
|
||||
{
|
||||
uint64_t kh;
|
||||
struct fuse_chan *ch;
|
||||
struct fuse_ll *f;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_req_ctx.h"
|
||||
#include "fuse_conn_info.hpp"
|
||||
|
||||
struct fuse_chan;
|
||||
|
||||
typedef struct fuse_req_t fuse_req_t;
|
||||
struct fuse_req_t
|
||||
{
|
||||
fuse_req_ctx_t ctx;
|
||||
struct fuse_chan *ch;
|
||||
unsigned int ioctl_64bit : 1;
|
||||
fuse_conn_info_t conn;
|
||||
};
|
||||
|
||||
fuse_req_t* fuse_req_alloc();
|
||||
void fuse_req_free(fuse_req_t *);
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_req_ctx_t fuse_req_ctx_t;
|
||||
struct fuse_req_ctx_t
|
||||
{
|
||||
uint32_t len;
|
||||
uint32_t opcode;
|
||||
uint64_t unique;
|
||||
uint64_t nodeid;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t pid;
|
||||
uint32_t umask;
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DIRENT_NAMELEN(X) (strlen((X)->name))
|
||||
|
||||
typedef struct linux_dirent64_t linux_dirent64_t;
|
||||
struct linux_dirent64_t
|
||||
{
|
||||
uint64_t ino;
|
||||
int64_t off;
|
||||
uint16_t reclen;
|
||||
uint8_t type;
|
||||
char name[];
|
||||
};
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "lock.h"
|
||||
#include "lfmp.h"
|
||||
|
||||
typedef struct node_s node_t;
|
||||
struct node_s
|
||||
typedef struct node_t node_t;
|
||||
struct node_t
|
||||
{
|
||||
node_t *name_next;
|
||||
node_t *id_next;
|
||||
@@ -26,6 +25,4 @@ struct node_s
|
||||
|
||||
node_t *node_alloc();
|
||||
void node_free(node_t*);
|
||||
int node_gc1();
|
||||
void node_gc();
|
||||
lfmp_t *node_lfmp();
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
|
||||
template<typename FuncType>
|
||||
[[nodiscard]]
|
||||
std::future<typename std::result_of<FuncType()>::type>
|
||||
std::future<std::invoke_result_t<FuncType>>
|
||||
enqueue_task(FuncType&& func_);
|
||||
|
||||
public:
|
||||
@@ -340,10 +340,10 @@ ThreadPool::enqueue_work(FuncType &&func_)
|
||||
template<typename FuncType>
|
||||
[[nodiscard]]
|
||||
inline
|
||||
std::future<typename std::result_of<FuncType()>::type>
|
||||
std::future<std::invoke_result_t<FuncType>>
|
||||
ThreadPool::enqueue_task(FuncType&& func_)
|
||||
{
|
||||
using TaskReturnType = typename std::result_of<FuncType()>::type;
|
||||
using TaskReturnType = std::invoke_result_t<FuncType>;
|
||||
using Promise = std::promise<TaskReturnType>;
|
||||
|
||||
Promise promise;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_lowlevel.hpp"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
+15
-1
@@ -861,11 +861,25 @@ fuse_syslog_fuse_init_out(const struct fuse_init_out *arg_)
|
||||
output = fmt::format("fuse_init_out:"
|
||||
" major={};"
|
||||
" minor={};"
|
||||
" max_readahead={};"
|
||||
" max_background={};"
|
||||
" congestion_threshold={};"
|
||||
" max_write={};"
|
||||
" time_gran={};"
|
||||
" max_pages={};"
|
||||
" map_alignment={};"
|
||||
" max_stack_depth={};"
|
||||
" flags=(",
|
||||
arg_->major,
|
||||
arg_->minor,
|
||||
arg_->max_pages);
|
||||
arg_->max_readahead,
|
||||
arg_->max_background,
|
||||
arg_->congestion_threshold,
|
||||
arg_->max_write,
|
||||
arg_->time_gran,
|
||||
arg_->max_pages,
|
||||
arg_->map_alignment,
|
||||
arg_->max_stack_depth);
|
||||
|
||||
for(uint64_t i = 0; i < (sizeof(flags)*8); i++)
|
||||
{
|
||||
|
||||
+861
-1216
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
#include "fuse_cfg.hpp"
|
||||
|
||||
fuse_cfg_t fuse_cfg;
|
||||
|
||||
bool
|
||||
fuse_cfg_t::valid_uid() const
|
||||
{
|
||||
return (uid != FUSE_CFG_INVALID_ID);
|
||||
}
|
||||
|
||||
bool
|
||||
fuse_cfg_t::valid_gid() const
|
||||
{
|
||||
return (gid != FUSE_CFG_INVALID_ID);
|
||||
}
|
||||
|
||||
bool
|
||||
fuse_cfg_t::valid_umask() const
|
||||
{
|
||||
return (umask != FUSE_CFG_INVALID_UMASK);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
static int g_READ_THREAD_COUNT = -1;
|
||||
static int g_PROCESS_THREAD_COUNT = -1;
|
||||
static int g_PROCESS_THREAD_QUEUE_DEPTH = -1;
|
||||
static std::string g_PIN_THREADS = {};
|
||||
|
||||
|
||||
int
|
||||
fuse_config_get_read_thread_count()
|
||||
{
|
||||
return g_READ_THREAD_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_read_thread_count(int const v_)
|
||||
{
|
||||
g_READ_THREAD_COUNT = v_;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_config_get_process_thread_count()
|
||||
{
|
||||
return g_PROCESS_THREAD_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_process_thread_count(int const v_)
|
||||
{
|
||||
g_PROCESS_THREAD_COUNT = v_;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_config_get_process_thread_queue_depth()
|
||||
{
|
||||
return g_PROCESS_THREAD_QUEUE_DEPTH;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_process_thread_queue_depth(int const v_)
|
||||
{
|
||||
g_PROCESS_THREAD_QUEUE_DEPTH = v_;
|
||||
}
|
||||
|
||||
std::string
|
||||
fuse_config_get_pin_threads()
|
||||
{
|
||||
return g_PIN_THREADS;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_pin_threads(std::string const v_)
|
||||
{
|
||||
g_PIN_THREADS = v_;
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "fuse_attr.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_dirents.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "linux_dirent64.h"
|
||||
#include "stat_utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 32KB - same as glibc getdents buffer size */
|
||||
#define DEFAULT_SIZE (1024 * 32)
|
||||
|
||||
static
|
||||
uint64_t
|
||||
round_up(const uint64_t number_,
|
||||
const uint64_t multiple_)
|
||||
{
|
||||
return (((number_ + multiple_ - 1) / multiple_) * multiple_);
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
align_uint64_t(uint64_t v_)
|
||||
{
|
||||
return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1));
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
fuse_dirent_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_dirent_t,name);
|
||||
rv += namelen_;
|
||||
rv = align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
fuse_direntplus_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_direntplus_t,dirent.name);
|
||||
rv += namelen_;
|
||||
rv = align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
fuse_dirents_buf_resize(fuse_dirents_t *d_,
|
||||
uint64_t size_)
|
||||
{
|
||||
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
|
||||
{
|
||||
uint64_t new_size;
|
||||
|
||||
new_size = round_up((kv_size(d_->data) + size_),DEFAULT_SIZE);
|
||||
|
||||
kv_resize(char,d_->data,new_size);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
|
||||
uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = fuse_dirent_size(namelen_);
|
||||
|
||||
rv = fuse_dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
|
||||
uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = fuse_direntplus_size(namelen_);
|
||||
|
||||
rv = fuse_dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_dirents_fill_attr(fuse_attr_t *attr_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
attr_->ino = st_->st_ino;
|
||||
attr_->size = st_->st_size;
|
||||
attr_->blocks = st_->st_blocks;
|
||||
attr_->atime = st_->st_atime;
|
||||
attr_->mtime = st_->st_mtime;
|
||||
attr_->ctime = st_->st_ctime;
|
||||
attr_->atimensec = ST_ATIM_NSEC(st_);
|
||||
attr_->mtimensec = ST_MTIM_NSEC(st_);
|
||||
attr_->ctimensec = ST_CTIM_NSEC(st_);
|
||||
attr_->mode = st_->st_mode;
|
||||
attr_->nlink = st_->st_nlink;
|
||||
attr_->uid = st_->st_uid;
|
||||
attr_->gid = st_->st_gid;
|
||||
attr_->rdev = st_->st_rdev;
|
||||
attr_->blksize = st_->st_blksize;
|
||||
}
|
||||
|
||||
fuse_dirent_t*
|
||||
fuse_dirent_next(fuse_dirent_t *cur_)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char*)cur_;
|
||||
buf += fuse_dirent_size(cur_->namelen);
|
||||
|
||||
return (fuse_dirent_t*)buf;
|
||||
}
|
||||
|
||||
fuse_direntplus_t*
|
||||
fuse_direntplus_next(fuse_direntplus_t *cur_)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char*)cur_;
|
||||
buf += fuse_direntplus_size(cur_->dirent.namelen);
|
||||
|
||||
return (fuse_direntplus_t*)buf;
|
||||
}
|
||||
|
||||
fuse_dirent_t*
|
||||
fuse_dirent_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
fuse_dirent_t *cur;
|
||||
fuse_dirent_t *end;
|
||||
|
||||
if(d_->type != NORMAL)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_dirent_t*)&kv_first(d_->data);
|
||||
end = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->ino == ino_)
|
||||
return cur;
|
||||
|
||||
cur = fuse_dirent_next(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fuse_direntplus_t*
|
||||
fuse_direntplus_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
fuse_direntplus_t *cur;
|
||||
fuse_direntplus_t *end;
|
||||
|
||||
if(d_->type != PLUS)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_direntplus_t*)&kv_first(d_->data);
|
||||
end = (fuse_direntplus_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->dirent.ino == ino_)
|
||||
return cur;
|
||||
|
||||
cur = fuse_direntplus_next(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void*
|
||||
fuse_dirents_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
switch(d_->type)
|
||||
{
|
||||
default:
|
||||
case UNSET:
|
||||
return NULL;
|
||||
case NORMAL:
|
||||
return fuse_dirent_find(d_,ino_);
|
||||
case PLUS:
|
||||
return fuse_direntplus_find(d_,ino_);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_convert_plus2normal(fuse_dirents_t *d_)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add(fuse_dirents_t *d_,
|
||||
const struct dirent *dirent_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = NORMAL;
|
||||
break;
|
||||
case NORMAL:
|
||||
break;
|
||||
case PLUS:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
d = fuse_dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->d_ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->d_type;
|
||||
memcpy(d->name,dirent_->d_name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_plus(fuse_dirents_t *d_,
|
||||
const struct dirent *dirent_,
|
||||
const uint64_t namelen_,
|
||||
const fuse_entry_t *entry_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
fuse_direntplus_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = PLUS;
|
||||
break;
|
||||
case NORMAL:
|
||||
return -EINVAL;
|
||||
case PLUS:
|
||||
break;
|
||||
}
|
||||
|
||||
d = fuse_dirents_direntplus_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->dirent.ino = dirent_->d_ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->d_type;
|
||||
memcpy(d->dirent.name,dirent_->d_name,namelen_);
|
||||
|
||||
d->entry = *entry_;
|
||||
|
||||
fuse_dirents_fill_attr(&d->attr,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||
const linux_dirent64_t *dirent_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = NORMAL;
|
||||
break;
|
||||
case NORMAL:
|
||||
break;
|
||||
case PLUS:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
d = fuse_dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->type;
|
||||
memcpy(d->name,dirent_->name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
const linux_dirent64_t *dirent_,
|
||||
const uint64_t namelen_,
|
||||
const fuse_entry_t *entry_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
fuse_direntplus_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = PLUS;
|
||||
break;
|
||||
case NORMAL:
|
||||
return -EINVAL;
|
||||
case PLUS:
|
||||
break;
|
||||
}
|
||||
|
||||
d = fuse_dirents_direntplus_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->dirent.ino = dirent_->ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->type;
|
||||
memcpy(d->dirent.name,dirent_->name,namelen_);
|
||||
|
||||
d->entry = *entry_;
|
||||
|
||||
fuse_dirents_fill_attr(&d->attr,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_reset(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->type = UNSET;
|
||||
kv_size(d_->data) = 0;
|
||||
kv_size(d_->offs) = 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_init(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_init(d_->data);
|
||||
kv_resize(char,d_->data,DEFAULT_SIZE);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kv_init(d_->offs);
|
||||
kv_resize(uint32_t,d_->offs,64);
|
||||
kv_push(uint32_t,d_->offs,0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_free(fuse_dirents_t *d_)
|
||||
{
|
||||
kv_destroy(d_->data);
|
||||
kv_destroy(d_->offs);
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "fs_dirent64.hpp"
|
||||
#include "fuse_attr.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_dirents.hpp"
|
||||
#include "fuse_entry.h"
|
||||
#include "stat_utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* 32KB - same as glibc getdents buffer size */
|
||||
#define DENTS_BUF_EXPAND_SIZE (1024 * 32)
|
||||
|
||||
static
|
||||
uint64_t
|
||||
_round_up(const uint64_t number_,
|
||||
const uint64_t multiple_)
|
||||
{
|
||||
return (((number_ + multiple_ - 1) / multiple_) * multiple_);
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
_align_uint64_t(uint64_t v_)
|
||||
{
|
||||
return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1));
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
_dirent_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_dirent_t,name);
|
||||
rv += namelen_;
|
||||
rv = _align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_dirents_buf_resize(fuse_dirents_t *d_,
|
||||
const uint64_t size_)
|
||||
{
|
||||
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
|
||||
{
|
||||
uint64_t new_size;
|
||||
|
||||
new_size = _round_up((kv_size(d_->data) + size_),DENTS_BUF_EXPAND_SIZE);
|
||||
|
||||
kv_resize(char,d_->data,new_size);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
fuse_dirent_t*
|
||||
_dirents_dirent_alloc(fuse_dirents_t *d_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = _dirent_size(namelen_);
|
||||
|
||||
rv = _dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add(fuse_dirents_t *d_,
|
||||
const dirent *de_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
d = _dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = de_->d_ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = de_->d_type;
|
||||
memcpy(d->name,de_->d_name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add(fuse_dirents_t *d_,
|
||||
const fs::dirent64 *de_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
d = _dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = de_->ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = de_->type;
|
||||
memcpy(d->name,de_->name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_reset(fuse_dirents_t *d_)
|
||||
{
|
||||
kv_size(d_->data) = 0;
|
||||
kv_size(d_->offs) = 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_init(fuse_dirents_t *d_)
|
||||
{
|
||||
kv_init(d_->data);
|
||||
kv_resize(char,d_->data,DENTS_BUF_EXPAND_SIZE);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kv_init(d_->offs);
|
||||
kv_resize(uint32_t,d_->offs,64);
|
||||
kv_push(uint32_t,d_->offs,0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_free(fuse_dirents_t *d_)
|
||||
{
|
||||
kv_destroy(d_->data);
|
||||
kv_destroy(d_->offs);
|
||||
}
|
||||
+9
-45
@@ -9,8 +9,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_msgbuf.h"
|
||||
#include "fuse_lowlevel.hpp"
|
||||
#include "fuse_msgbuf_t.h"
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
@@ -27,62 +27,26 @@ struct fuse_session
|
||||
|
||||
void (*destroy)(void *data);
|
||||
|
||||
struct fuse_ll *f;
|
||||
volatile int exited;
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
struct fuse_req
|
||||
{
|
||||
struct fuse_ll *f;
|
||||
uint64_t unique;
|
||||
struct fuse_ctx ctx;
|
||||
struct fuse_chan *ch;
|
||||
unsigned int ioctl_64bit : 1;
|
||||
};
|
||||
|
||||
struct fuse_notify_req
|
||||
{
|
||||
uint64_t unique;
|
||||
void (*reply)(struct fuse_notify_req *,
|
||||
fuse_req_t,
|
||||
fuse_req_t*,
|
||||
uint64_t,
|
||||
const void *);
|
||||
struct fuse_notify_req *next;
|
||||
struct fuse_notify_req *prev;
|
||||
};
|
||||
|
||||
struct fuse_ll
|
||||
{
|
||||
int debug;
|
||||
int no_remote_posix_lock;
|
||||
int no_remote_flock;
|
||||
int big_writes;
|
||||
struct fuse_lowlevel_ops op;
|
||||
void *userdata;
|
||||
uid_t owner;
|
||||
struct fuse_conn_info conn;
|
||||
pthread_mutex_t lock;
|
||||
int got_init;
|
||||
int got_destroy;
|
||||
pthread_key_t pipe_key;
|
||||
int broken_splice_nonblock;
|
||||
uint64_t notify_ctr;
|
||||
struct fuse_notify_req notify_list;
|
||||
};
|
||||
|
||||
struct fuse_cmd
|
||||
{
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size);
|
||||
struct fuse *fuse_new_common(struct fuse_chan *ch,
|
||||
struct fuse_args *args,
|
||||
const struct fuse_operations *op);
|
||||
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd);
|
||||
|
||||
@@ -100,10 +64,10 @@ int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
|
||||
void fuse_free_req(fuse_req_t req);
|
||||
|
||||
|
||||
struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
struct fuse *fuse_setup_common(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
char **mountpoint,
|
||||
int *fd);
|
||||
|
||||
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "fuse_msgbuf.h"
|
||||
#include "fuse_msgbuf_t.h"
|
||||
|
||||
int fuse_receive_buf(struct fuse_session *se,
|
||||
fuse_msgbuf_t *msgbuf);
|
||||
|
||||
@@ -12,11 +12,10 @@
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_lowlevel.hpp"
|
||||
|
||||
#include "fuse_config.hpp"
|
||||
#include "fuse_cfg.hpp"
|
||||
#include "fuse_msgbuf.hpp"
|
||||
#include "fuse_ll.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
@@ -335,11 +334,11 @@ fuse_loop_mt(struct fuse *f_)
|
||||
MaintenanceThread::setup();
|
||||
fuse_populate_maintenance_thread(f_);
|
||||
|
||||
res = fuse_session_loop_mt(fuse_get_session(f_),
|
||||
fuse_config_get_read_thread_count(),
|
||||
fuse_config_get_process_thread_count(),
|
||||
fuse_config_get_process_thread_queue_depth(),
|
||||
fuse_config_get_pin_threads());
|
||||
res = fuse_session_loop_mt(fuse_get_session(),
|
||||
fuse_cfg.read_thread_count,
|
||||
fuse_cfg.process_thread_count,
|
||||
fuse_cfg.process_thread_queue_depth,
|
||||
fuse_cfg.pin_threads);
|
||||
|
||||
MaintenanceThread::stop();
|
||||
|
||||
|
||||
+347
-561
File diff suppressed because it is too large
Load Diff
+24
-12
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "fuse_msgbuf.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
@@ -42,22 +43,34 @@ msgbuf_get_bufsize()
|
||||
return g_BUFSIZE;
|
||||
}
|
||||
|
||||
u32
|
||||
msgbuf_get_pagesize()
|
||||
{
|
||||
return g_PAGESIZE;
|
||||
}
|
||||
|
||||
// +1 page so that it is used for the "header" of the allocation as
|
||||
// well as to allow for offseting for write requests to be page
|
||||
// aligned. +1 again for fuse header as the max_pages value is for the
|
||||
// body.
|
||||
void
|
||||
msgbuf_set_bufsize(const uint32_t size_in_pages_)
|
||||
{
|
||||
g_BUFSIZE = ((size_in_pages_ + 1) * g_PAGESIZE);
|
||||
g_BUFSIZE = ((size_in_pages_ + 2) * g_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
|
||||
_msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_->mem = (char*)msgbuf_;
|
||||
msgbuf_->mem += g_PAGESIZE;
|
||||
msgbuf_->size = (g_BUFSIZE - g_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
|
||||
_msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_->mem = (char*)msgbuf_;
|
||||
msgbuf_->mem += g_PAGESIZE;
|
||||
@@ -69,24 +82,24 @@ msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
|
||||
static
|
||||
__attribute__((constructor))
|
||||
void
|
||||
msgbuf_constructor()
|
||||
_msgbuf_constructor()
|
||||
{
|
||||
g_PAGESIZE = sysconf(_SC_PAGESIZE);
|
||||
// FUSE_MAX_MAX_PAGES for payload + 1 for message header
|
||||
msgbuf_set_bufsize(FUSE_DEFAULT_MAX_MAX_PAGES + 1);
|
||||
|
||||
msgbuf_set_bufsize(FUSE_DEFAULT_MAX_MAX_PAGES);
|
||||
}
|
||||
|
||||
static
|
||||
__attribute__((destructor))
|
||||
void
|
||||
msgbuf_destroy()
|
||||
_msgbuf_destructor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
page_aligned_malloc(const uint64_t size_)
|
||||
_page_aligned_malloc(const uint64_t size_)
|
||||
{
|
||||
int rv;
|
||||
void *buf = NULL;
|
||||
@@ -111,7 +124,7 @@ _msgbuf_alloc(msgbuf_setup_func_t setup_func_)
|
||||
{
|
||||
g_MUTEX.unlock();
|
||||
|
||||
msgbuf = (fuse_msgbuf_t*)page_aligned_malloc(g_BUFSIZE);
|
||||
msgbuf = (fuse_msgbuf_t*)_page_aligned_malloc(g_BUFSIZE);
|
||||
if(msgbuf == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -135,20 +148,19 @@ _msgbuf_alloc(msgbuf_setup_func_t setup_func_)
|
||||
fuse_msgbuf_t*
|
||||
msgbuf_alloc()
|
||||
{
|
||||
return _msgbuf_alloc(msgbuf_write_align);
|
||||
return _msgbuf_alloc(_msgbuf_write_align);
|
||||
}
|
||||
|
||||
fuse_msgbuf_t*
|
||||
msgbuf_alloc_page_aligned()
|
||||
{
|
||||
return _msgbuf_alloc(msgbuf_page_align);
|
||||
return _msgbuf_alloc(_msgbuf_page_align);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
msgbuf_destroy(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
// free(msgbuf_->mem);
|
||||
free(msgbuf_);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "fuse_req.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
typedef struct stack_t stack_t;
|
||||
struct stack_t
|
||||
{
|
||||
stack_t *next;
|
||||
};
|
||||
|
||||
thread_local static stack_t *g_stack = NULL;
|
||||
|
||||
fuse_req_t*
|
||||
fuse_req_alloc()
|
||||
{
|
||||
if(g_stack == NULL)
|
||||
return (fuse_req_t*)calloc(1,sizeof(fuse_req_t));
|
||||
|
||||
fuse_req_t *req;
|
||||
|
||||
req = (fuse_req_t*)g_stack;
|
||||
g_stack = g_stack->next;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_req_free(fuse_req_t *req_)
|
||||
{
|
||||
stack_t *stack;
|
||||
|
||||
stack = (stack_t*)req_;
|
||||
stack->next = g_stack;
|
||||
g_stack = stack;
|
||||
}
|
||||
@@ -26,22 +26,20 @@ struct fuse_chan
|
||||
};
|
||||
|
||||
struct fuse_session *
|
||||
fuse_session_new(void *data,
|
||||
void *receive_buf,
|
||||
fuse_session_new(void *receive_buf,
|
||||
void *process_buf,
|
||||
void *destroy)
|
||||
{
|
||||
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
|
||||
if (se == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate session\n");
|
||||
return NULL;
|
||||
}
|
||||
struct fuse_session *se = (struct fuse_session *)calloc(1,sizeof(*se));
|
||||
if(se == NULL)
|
||||
{
|
||||
fprintf(stderr, "fuse: failed to allocate session\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(se, 0, sizeof(*se));
|
||||
se->f = data;
|
||||
se->receive_buf = receive_buf;
|
||||
se->process_buf = process_buf;
|
||||
se->destroy = destroy;
|
||||
se->receive_buf = (int(*)(fuse_session*,fuse_msgbuf_t*))receive_buf;
|
||||
se->process_buf = (void(*)(fuse_session*,const fuse_msgbuf_t*))process_buf;
|
||||
se->destroy = (void(*)(void*))destroy;
|
||||
|
||||
return se;
|
||||
}
|
||||
@@ -67,7 +65,7 @@ void fuse_session_remove_chan(struct fuse_chan *ch)
|
||||
void
|
||||
fuse_session_destroy(struct fuse_session *se)
|
||||
{
|
||||
se->destroy(se->f);
|
||||
se->destroy(NULL);
|
||||
if(se->ch != NULL)
|
||||
fuse_chan_destroy(se->ch);
|
||||
free(se);
|
||||
@@ -90,12 +88,6 @@ fuse_session_exit(struct fuse_session *se_)
|
||||
se_->exited = 1;
|
||||
}
|
||||
|
||||
void*
|
||||
fuse_session_data(struct fuse_session *se)
|
||||
{
|
||||
return se->f;
|
||||
}
|
||||
|
||||
struct fuse_chan *
|
||||
fuse_chan_new(int fd,
|
||||
size_t bufsize)
|
||||
@@ -6,7 +6,7 @@
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_lowlevel.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_lowlevel.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -41,74 +41,43 @@ struct fuse_opt fuse_helper_opts[] =
|
||||
FUSE_HELPER_OPT("-f", foreground),
|
||||
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
|
||||
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void usage(const char *progname)
|
||||
static
|
||||
int
|
||||
fuse_helper_opt_proc(void *data,
|
||||
const char *arg,
|
||||
int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s mountpoint [options]\n\n", progname);
|
||||
fprintf(stderr,
|
||||
"general options:\n"
|
||||
" -o opt,[opt...] mount options\n"
|
||||
" -h --help print help\n"
|
||||
" -V --version print version\n"
|
||||
"\n");
|
||||
}
|
||||
struct helper_opts *hopts = (helper_opts*)data;
|
||||
|
||||
static void helper_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"FUSE options:\n"
|
||||
" -d -o debug enable debug output (implies -f)\n"
|
||||
" -f foreground operation\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
switch (key)
|
||||
{
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if(!hopts->mountpoint)
|
||||
{
|
||||
char mountpoint[PATH_MAX];
|
||||
if(realpath(arg, mountpoint) == NULL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"fuse: bad mount point `%s': %s\n",
|
||||
arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct helper_opts *hopts = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_HELP:
|
||||
usage(outargs->argv[0]);
|
||||
/* fall through */
|
||||
|
||||
case KEY_HELP_NOHEADER:
|
||||
helper_help();
|
||||
return fuse_opt_add_arg(outargs, "-h");
|
||||
|
||||
case KEY_VERSION:
|
||||
return 1;
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (!hopts->mountpoint) {
|
||||
char mountpoint[PATH_MAX];
|
||||
if (realpath(arg, mountpoint) == NULL) {
|
||||
fprintf(stderr,
|
||||
"fuse: bad mount point `%s': %s\n",
|
||||
arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
|
||||
} else {
|
||||
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 1;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,11 +250,10 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
}
|
||||
|
||||
struct fuse *
|
||||
struct fuse*
|
||||
fuse_setup_common(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *fd)
|
||||
{
|
||||
@@ -305,7 +273,7 @@ fuse_setup_common(int argc,
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
fuse = fuse_new_common(ch, &args, op, op_size);
|
||||
fuse = fuse_new(ch, &args, op);
|
||||
fuse_opt_free_args(&args);
|
||||
if (fuse == NULL)
|
||||
goto err_unmount;
|
||||
@@ -314,7 +282,7 @@ fuse_setup_common(int argc,
|
||||
if (res == -1)
|
||||
goto err_unmount;
|
||||
|
||||
res = fuse_set_signal_handlers(fuse_get_session(fuse));
|
||||
res = fuse_set_signal_handlers(fuse_get_session());
|
||||
if (res == -1)
|
||||
goto err_unmount;
|
||||
|
||||
@@ -325,75 +293,59 @@ fuse_setup_common(int argc,
|
||||
|
||||
err_unmount:
|
||||
fuse_unmount_common(*mountpoint, ch);
|
||||
if (fuse)
|
||||
fuse_destroy(fuse);
|
||||
err_free:
|
||||
free(*mountpoint);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct fuse *fuse_setup(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
struct fuse *fuse_setup(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
char **mountpoint)
|
||||
{
|
||||
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
|
||||
return fuse_setup_common(argc,
|
||||
argv,
|
||||
op,
|
||||
mountpoint,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
|
||||
static void fuse_teardown_common(char *mountpoint)
|
||||
{
|
||||
struct fuse_session *se = fuse_get_session(fuse);
|
||||
struct fuse_session *se = fuse_get_session();
|
||||
struct fuse_chan *ch = se->ch;
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
fuse_destroy(fuse);
|
||||
free(mountpoint);
|
||||
}
|
||||
|
||||
void fuse_teardown(struct fuse *fuse, char *mountpoint)
|
||||
void fuse_teardown(char *mountpoint)
|
||||
{
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
fuse_teardown_common(mountpoint);
|
||||
}
|
||||
|
||||
static int fuse_main_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size)
|
||||
int
|
||||
fuse_main(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op)
|
||||
{
|
||||
struct fuse *fuse;
|
||||
char *mountpoint;
|
||||
int res;
|
||||
|
||||
fuse = fuse_setup_common(argc, argv, op, op_size,
|
||||
fuse = fuse_setup_common(argc,
|
||||
argv,
|
||||
op,
|
||||
&mountpoint,
|
||||
NULL);
|
||||
if (fuse == NULL)
|
||||
if(fuse == NULL)
|
||||
return 1;
|
||||
|
||||
res = fuse_loop_mt(fuse);
|
||||
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
if (res == -1)
|
||||
fuse_teardown_common(mountpoint);
|
||||
if(res == -1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_main_real(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size)
|
||||
{
|
||||
return fuse_main_common(argc, argv, op, op_size);
|
||||
}
|
||||
|
||||
#undef fuse_main
|
||||
int fuse_main(void);
|
||||
int fuse_main(void)
|
||||
{
|
||||
fprintf(stderr, "fuse_main(): This function does not exist\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fuse_version(void)
|
||||
{
|
||||
return FUSE_VERSION;
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_opt.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
+117
-131
@@ -49,14 +49,11 @@ enum {
|
||||
KEY_FUSERMOUNT_OPT,
|
||||
KEY_SUBTYPE_OPT,
|
||||
KEY_MTAB_OPT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
KEY_RO
|
||||
};
|
||||
|
||||
struct mount_opts {
|
||||
int allow_other;
|
||||
int ishelp;
|
||||
int flags;
|
||||
int auto_unmount;
|
||||
int blkdev;
|
||||
@@ -70,59 +67,54 @@ struct mount_opts {
|
||||
|
||||
#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
|
||||
|
||||
static const struct fuse_opt fuse_mount_opts[] = {
|
||||
FUSE_MOUNT_OPT("allow_other", allow_other),
|
||||
FUSE_MOUNT_OPT("blkdev", blkdev),
|
||||
FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
|
||||
FUSE_MOUNT_OPT("fsname=%s", fsname),
|
||||
FUSE_MOUNT_OPT("subtype=%s", subtype),
|
||||
FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
|
||||
FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("context=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
|
||||
FUSE_OPT_KEY("-r", KEY_RO),
|
||||
FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("async", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void mount_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o auto_unmount auto unmount on process termination\n"
|
||||
" -o default_permissions enable permission checking by kernel\n"
|
||||
" -o fsname=NAME set filesystem name\n"
|
||||
" -o subtype=NAME set filesystem type\n"
|
||||
" -o large_read issue large read requests (2.4 only)\n"
|
||||
" -o max_read=N set maximum size of read requests\n"
|
||||
"\n");
|
||||
}
|
||||
static
|
||||
const
|
||||
struct fuse_opt fuse_mount_opts[] =
|
||||
{
|
||||
FUSE_MOUNT_OPT("allow_other", allow_other),
|
||||
FUSE_MOUNT_OPT("blkdev", blkdev),
|
||||
FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
|
||||
FUSE_MOUNT_OPT("fsname=%s", fsname),
|
||||
FUSE_MOUNT_OPT("subtype=%s", subtype),
|
||||
FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
|
||||
FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("context=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
|
||||
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
|
||||
FUSE_OPT_KEY("-r", KEY_RO),
|
||||
FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("async", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("relatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("norelatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("lazytime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nolazytime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("diratime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("strictatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void exec_fusermount(const char *argv[])
|
||||
{
|
||||
@@ -132,56 +124,63 @@ static void exec_fusermount(const char *argv[])
|
||||
execvp(FUSERMOUNT_PROG, (char **) argv);
|
||||
}
|
||||
|
||||
static void mount_version(void)
|
||||
{
|
||||
int pid = fork();
|
||||
if (!pid) {
|
||||
const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
|
||||
exec_fusermount(argv);
|
||||
_exit(1);
|
||||
} else if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
};
|
||||
|
||||
static const struct mount_flags mount_flags[] = {
|
||||
{"rw", MS_RDONLY, 0},
|
||||
{"ro", MS_RDONLY, 1},
|
||||
{"suid", MS_NOSUID, 0},
|
||||
{"nosuid", MS_NOSUID, 1},
|
||||
{"dev", MS_NODEV, 0},
|
||||
{"nodev", MS_NODEV, 1},
|
||||
{"exec", MS_NOEXEC, 0},
|
||||
{"noexec", MS_NOEXEC, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0},
|
||||
{"sync", MS_SYNCHRONOUS, 1},
|
||||
{"atime", MS_NOATIME, 0},
|
||||
{"noatime", MS_NOATIME, 1},
|
||||
static
|
||||
const
|
||||
struct mount_flags
|
||||
mount_flags[] =
|
||||
{
|
||||
{"rw", MS_RDONLY, 0},
|
||||
{"ro", MS_RDONLY, 1},
|
||||
{"suid", MS_NOSUID, 0},
|
||||
{"nosuid", MS_NOSUID, 1},
|
||||
{"dev", MS_NODEV, 0},
|
||||
{"nodev", MS_NODEV, 1},
|
||||
{"exec", MS_NOEXEC, 0},
|
||||
{"noexec", MS_NOEXEC, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0},
|
||||
{"sync", MS_SYNCHRONOUS, 1},
|
||||
{"atime", MS_NOATIME, 0},
|
||||
{"noatime", MS_NOATIME, 1},
|
||||
{"relatime", MS_RELATIME, 1},
|
||||
{"norelatime", MS_RELATIME, 0},
|
||||
{"lazytime", MS_LAZYTIME, 1},
|
||||
{"nolazytime", MS_LAZYTIME, 0},
|
||||
{"diratime", MS_NODIRATIME, 0},
|
||||
{"nodiratime", MS_NODIRATIME, 1},
|
||||
{"strictatime", MS_STRICTATIME, 1},
|
||||
{"nostrictatime",MS_STRICTATIME, 0},
|
||||
#ifndef __NetBSD__
|
||||
{"dirsync", MS_DIRSYNC, 1},
|
||||
{"dirsync", MS_DIRSYNC, 1},
|
||||
#endif
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static void set_mount_flag(const char *s, int *flags)
|
||||
static
|
||||
void
|
||||
set_mount_flag(const char *s,
|
||||
int *flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if (strcmp(opt, s) == 0) {
|
||||
if (mount_flags[i].on)
|
||||
*flags |= mount_flags[i].flag;
|
||||
else
|
||||
*flags &= ~mount_flags[i].flag;
|
||||
return;
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++)
|
||||
{
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if(strcmp(opt, s) == 0)
|
||||
{
|
||||
if(mount_flags[i].on)
|
||||
*flags |= mount_flags[i].flag;
|
||||
else
|
||||
*flags &= ~mount_flags[i].flag;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "fuse: internal error, can't find mount flag\n");
|
||||
abort();
|
||||
}
|
||||
@@ -189,38 +188,29 @@ static void set_mount_flag(const char *s, int *flags)
|
||||
static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct mount_opts *mo = data;
|
||||
struct mount_opts *mo = (mount_opts*)data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
case KEY_KERN_FLAG:
|
||||
set_mount_flag(arg, &mo->flags);
|
||||
return 0;
|
||||
switch (key)
|
||||
{
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
case KEY_KERN_FLAG:
|
||||
set_mount_flag(arg, &mo->flags);
|
||||
return 0;
|
||||
|
||||
case KEY_KERN_OPT:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
case KEY_KERN_OPT:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
|
||||
case KEY_FUSERMOUNT_OPT:
|
||||
return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
|
||||
case KEY_FUSERMOUNT_OPT:
|
||||
return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
|
||||
|
||||
case KEY_SUBTYPE_OPT:
|
||||
return fuse_opt_add_opt(&mo->subtype_opt, arg);
|
||||
case KEY_SUBTYPE_OPT:
|
||||
return fuse_opt_add_opt(&mo->subtype_opt, arg);
|
||||
|
||||
case KEY_MTAB_OPT:
|
||||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
case KEY_MTAB_OPT:
|
||||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -436,11 +426,11 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
|
||||
if (res == -1)
|
||||
goto out_close;
|
||||
|
||||
source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
|
||||
(mo->subtype ? strlen(mo->subtype) : 0) +
|
||||
strlen(devname) + 32);
|
||||
source = (char*)malloc((mo->fsname ? strlen(mo->fsname) : 0) +
|
||||
(mo->subtype ? strlen(mo->subtype) : 0) +
|
||||
strlen(devname) + 32);
|
||||
|
||||
type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
|
||||
type = (char*)malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
|
||||
if (!type || !source) {
|
||||
fprintf(stderr, "fuse: failed to allocate memory\n");
|
||||
goto out_close;
|
||||
@@ -542,14 +532,10 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
memset(&mo, 0, sizeof(mo));
|
||||
mo.flags = MS_NOSUID | MS_NODEV;
|
||||
|
||||
if (args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
if(args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
|
||||
res = 0;
|
||||
if (mo.ishelp)
|
||||
goto out;
|
||||
|
||||
res = -1;
|
||||
if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
|
||||
goto out;
|
||||
|
||||
+67
-35
@@ -1,60 +1,92 @@
|
||||
#include "node.hpp"
|
||||
|
||||
#include "lfmp.h"
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
static lfmp_t g_NODE_FMP;
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
static
|
||||
__attribute__((constructor))
|
||||
void
|
||||
node_constructor()
|
||||
#include "fmt/core.h"
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
struct stack_t
|
||||
{
|
||||
lfmp_init(&g_NODE_FMP,sizeof(node_t),256);
|
||||
}
|
||||
stack_t *next;
|
||||
};
|
||||
|
||||
static
|
||||
__attribute__((destructor))
|
||||
void
|
||||
node_destructor()
|
||||
struct StackInfo
|
||||
{
|
||||
lfmp_destroy(&g_NODE_FMP);
|
||||
}
|
||||
stack_t *stack;
|
||||
bool *should_gc;
|
||||
};
|
||||
|
||||
thread_local static stack_t *g_stack = NULL;
|
||||
thread_local bool g_should_gc = false;
|
||||
thread_local bool g_initialized = false;
|
||||
|
||||
static std::mutex g_mutex;
|
||||
static std::vector<StackInfo> g_all_stacks;
|
||||
|
||||
|
||||
node_t*
|
||||
node_alloc()
|
||||
{
|
||||
return (node_t*)lfmp_calloc(&g_NODE_FMP);
|
||||
if(g_initialized == false)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(g_mutex);
|
||||
g_all_stacks.push_back({g_stack,&g_should_gc});
|
||||
g_initialized = true;
|
||||
}
|
||||
|
||||
if(g_should_gc)
|
||||
{
|
||||
int i;
|
||||
stack_t *s;
|
||||
|
||||
i = 0;
|
||||
s = g_stack;
|
||||
while(s)
|
||||
{
|
||||
i++;
|
||||
s = s->next;
|
||||
}
|
||||
fmt::print("{} {}\n",
|
||||
gettid(),
|
||||
i);
|
||||
|
||||
g_should_gc = false;
|
||||
}
|
||||
|
||||
if(g_stack == NULL)
|
||||
return (node_t*)calloc(1,sizeof(node_t));
|
||||
|
||||
node_t *node;
|
||||
|
||||
node = (node_t*)g_stack;
|
||||
g_stack = g_stack->next;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
node_free(node_t *node_)
|
||||
{
|
||||
lfmp_free(&g_NODE_FMP,node_);
|
||||
stack_t *stack;
|
||||
|
||||
stack = (stack_t*)node_;
|
||||
stack->next = g_stack;
|
||||
g_stack = stack;
|
||||
}
|
||||
|
||||
int
|
||||
node_gc1()
|
||||
{
|
||||
return lfmp_gc(&g_NODE_FMP);
|
||||
}
|
||||
|
||||
void
|
||||
node_gc()
|
||||
{
|
||||
int rv;
|
||||
int fails;
|
||||
std::lock_guard<std::mutex> _(g_mutex);
|
||||
|
||||
fails = 0;
|
||||
do
|
||||
for(auto &info : g_all_stacks)
|
||||
{
|
||||
rv = node_gc1();
|
||||
if(rv == 0)
|
||||
fails++;
|
||||
} while(rv || (fails < 3));
|
||||
}
|
||||
|
||||
lfmp_t*
|
||||
node_lfmp()
|
||||
{
|
||||
return &g_NODE_FMP;
|
||||
*info.should_gc = true;
|
||||
}
|
||||
}
|
||||
|
||||
+58
-41
@@ -579,49 +579,66 @@ static int begins_with(const char *s, const char *beg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mount_flags {
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
int safe;
|
||||
};
|
||||
|
||||
static struct mount_flags mount_flags[] = {
|
||||
{"rw", MS_RDONLY, 0, 1},
|
||||
{"ro", MS_RDONLY, 1, 1},
|
||||
{"suid", MS_NOSUID, 0, 0},
|
||||
{"nosuid", MS_NOSUID, 1, 1},
|
||||
{"dev", MS_NODEV, 0, 0},
|
||||
{"nodev", MS_NODEV, 1, 1},
|
||||
{"exec", MS_NOEXEC, 0, 1},
|
||||
{"noexec", MS_NOEXEC, 1, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0, 1},
|
||||
{"sync", MS_SYNCHRONOUS, 1, 1},
|
||||
{"atime", MS_NOATIME, 0, 1},
|
||||
{"noatime", MS_NOATIME, 1, 1},
|
||||
{"dirsync", MS_DIRSYNC, 1, 1},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
|
||||
struct mount_flags
|
||||
{
|
||||
int i;
|
||||
const char *opt;
|
||||
unsigned long flag;
|
||||
int on;
|
||||
int safe;
|
||||
};
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++) {
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
|
||||
*on = mount_flags[i].on;
|
||||
*flag = mount_flags[i].flag;
|
||||
if (!mount_flags[i].safe && getuid() != 0) {
|
||||
*flag = 0;
|
||||
fprintf(stderr,
|
||||
"%s: unsafe option %s ignored\n",
|
||||
progname, opt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
static
|
||||
struct mount_flags
|
||||
mount_flags[] =
|
||||
{
|
||||
{"rw", MS_RDONLY, 0, 1},
|
||||
{"ro", MS_RDONLY, 1, 1},
|
||||
{"suid", MS_NOSUID, 0, 0},
|
||||
{"nosuid", MS_NOSUID, 1, 1},
|
||||
{"dev", MS_NODEV, 0, 0},
|
||||
{"nodev", MS_NODEV, 1, 1},
|
||||
{"exec", MS_NOEXEC, 0, 1},
|
||||
{"noexec", MS_NOEXEC, 1, 1},
|
||||
{"async", MS_SYNCHRONOUS, 0, 1},
|
||||
{"sync", MS_SYNCHRONOUS, 1, 1},
|
||||
{"atime", MS_NOATIME, 0, 1},
|
||||
{"noatime", MS_NOATIME, 1, 1},
|
||||
{"dirsync", MS_DIRSYNC, 1, 1},
|
||||
{"relatime", MS_RELATIME, 1, 1},
|
||||
{"norelatime", MS_RELATIME, 0, 1},
|
||||
{"lazytime", MS_LAZYTIME, 1, 1},
|
||||
{"nolazytime", MS_LAZYTIME, 0, 1},
|
||||
{"diratime", MS_NODIRATIME, 0, 1},
|
||||
{"nodiratime", MS_NODIRATIME, 1, 1},
|
||||
{"strictatime", MS_STRICTATIME, 1, 1},
|
||||
{"nostrictatime", MS_STRICTATIME, 0, 1},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
static
|
||||
int
|
||||
find_mount_flag(const char *s, unsigned len, int *on, int *flag)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; mount_flags[i].opt != NULL; i++)
|
||||
{
|
||||
const char *opt = mount_flags[i].opt;
|
||||
if(strlen(opt) == len && strncmp(opt, s, len) == 0)
|
||||
{
|
||||
*on = mount_flags[i].on;
|
||||
*flag = mount_flags[i].flag;
|
||||
if(!mount_flags[i].safe && getuid() != 0)
|
||||
{
|
||||
*flag = 0;
|
||||
fprintf(stderr,
|
||||
"%s: unsafe option %s ignored\n",
|
||||
progname, opt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_option(char **optsp, const char *opt, unsigned expand)
|
||||
|
||||
@@ -41,10 +41,10 @@ transparently enable page caching when mmap is requested. This means
|
||||
it should be safe to set `cache.files=off`. However, on Linux v6.5 and
|
||||
below you will need to configure `cache.files` as you need.
|
||||
|
||||
If [passthrough](passthrough.md) is enabled so must be page
|
||||
caching. mergerfs will set `cache.files=auto-full` if `passthrough` is
|
||||
enabled. And when using `passthrough` the there is no double page
|
||||
caching since it is in fact passing through the IO.
|
||||
If [passthrough.io](passthrough.md) is enabled so must page
|
||||
caching. mergerfs will set `cache.files=auto-full` if `passthrough.io`
|
||||
is enabled. And when using `passthrough.io` the there is no double
|
||||
page caching since it is in fact passing through the IO.
|
||||
|
||||
|
||||
[^1]: This is not unique to mergerfs and affects all FUSE
|
||||
@@ -73,9 +73,9 @@ little risk in setting the value much higher. Especially if there are
|
||||
no out-of-band changes.
|
||||
|
||||
|
||||
## cache.negative_entry
|
||||
## cache.negative-entry
|
||||
|
||||
* `cache.negative_entry=UINT`: Sets the number of seconds to cache
|
||||
* `cache.negative-entry=UINT`: Sets the number of seconds to cache
|
||||
negative entry queries. Defaults to `1`.
|
||||
|
||||
This is a cache for negative entry query responses. Such as when a
|
||||
@@ -172,8 +172,8 @@ the FUSE writeback cache small writes *may* be aggregated by the
|
||||
kernel and then sent to mergerfs as one larger request. This can
|
||||
greatly improve the throughput for apps which write to files
|
||||
inefficiently. The amount the kernel can aggregate is limited by the
|
||||
size of a FUSE message. Read the fuse_msg_size section for more
|
||||
details.
|
||||
size of a FUSE message. Read the [fuse-msg-size](fuse-msg-size.md)
|
||||
section for more details.
|
||||
|
||||
There is a side effect as a result of enabling writeback
|
||||
caching. Underlying files won't ever be opened with O_APPEND or
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
These are old, deprecated options which may no longer have any
|
||||
function or have been replaced. **They should not be used.**
|
||||
|
||||
* **direct_io**: Bypass page cache. Use `cache.files=off`
|
||||
instead.
|
||||
* **kernel_cache**: Do not invalidate data cache on file open. Use
|
||||
`cache.files=full` instead.
|
||||
* **auto_cache**: Invalidate data cache if file mtime or
|
||||
size change. Use `cache.files=auto-full` instead. (default: false)
|
||||
* **async_read**: Perform reads asynchronously. Use
|
||||
`async_read=true` instead.
|
||||
* **sync_read**: Perform reads synchronously. Use
|
||||
`async_read=false` instead.
|
||||
* **splice_read**: Does nothing.
|
||||
* **splice_write**: Does nothing.
|
||||
* **splice_move**: Does nothing.
|
||||
* **allow_other**: mergerfs v2.35.0 and above sets this FUSE option
|
||||
automatically if running as root.
|
||||
* **use_ino**: Effectively replaced with `inodecalc`.
|
||||
* **async_read**: Use `async-read=true`.
|
||||
* **atomic_o_trunc**: Does nothing.
|
||||
* **big_writes**: Does nothing.
|
||||
* **attr_timeout**: Use [cache.attr](cache.md).
|
||||
* **auto_cache**: Use [cache.files=auto-full](cache.md).
|
||||
* **direct_io**: Use [cache.files=off](cache.md).
|
||||
* **entry_timeout**: Use [cache.entry](cache.md).
|
||||
* **hard_remove**: Does nothing.
|
||||
* **kernel_cache**: Use [cache.files=full](cache.md).
|
||||
* **negative_entry**: Use [cache.negative-entry](cache.md).
|
||||
* **nonempty**: Does nothing.
|
||||
* **splice_move**: Does nothing.
|
||||
* **splice_read**: Does nothing.
|
||||
* **splice_write**: Does nothing.
|
||||
* **sync_read**: Use `async-read=false`.
|
||||
* **use_ino**: Use [inodecalc](inodecalc.md).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# func.readdir
|
||||
|
||||
Defaults to `seq`
|
||||
* type: `STR`
|
||||
* default: `seq`
|
||||
* examples: `func.readdir=seq`, `func.readdir=cor:4`
|
||||
|
||||
Examples: `func.readdir=seq`, `func.readdir=cor:4`
|
||||
|
||||
`readdir` has policies to control how it reads directory content.
|
||||
`readdir` has policies to control how to read directory content.
|
||||
|
||||
| Policy | Description |
|
||||
| ------ | ----------- |
|
||||
@@ -12,7 +12,7 @@ Examples: `func.readdir=seq`, `func.readdir=cor:4`
|
||||
| cosr:N:M | "concurrent open, sequential read" : Concurrently open branch directories using a thread pool and process them in the order defined in `branches`. This keeps memory and CPU usage low while also reducing the time spent waiting on branches to respond. `N` is the number of threads. If negative it will be the core count divided by `abs(N)`. `M` is the queue depth. If either value is `0` it will be decided based on system configuration. |
|
||||
| cosr:N | cosr:N:M with M = 0 |
|
||||
| cosr | cosr:N:M with N = 0 and M = 0 |
|
||||
| cor:N:M | "concurrent open and read" : Concurrently open branch directories and immediately start reading their contents using a thread pool. This will result in slightly higher memory and CPU usage but reduced latency. Particularly when using higher latency / slower speed network filesystem branches. Unlike `seq` and `cosr` the order of files could change due the async nature of the thread pool. This should not be a problem since the order of files listed in not guaranteed. `N` is the number of threads. If negative it will be the core count divided by `abs(N)`. `M` is the queue depth. If either value is `0` it will be decided based on system configuration. |
|
||||
| cor:N:M | "concurrent open and read" : Concurrently open branch directories and immediately start reading their contents using a thread pool. This will result in slightly higher memory and CPU usage but reduced latency. Particularly when using higher latency / slower speed network filesystem branches. Unlike `seq` and `cosr` the order of files could change due the async nature of the thread pool. This should not be a problem since the order of files returned in not guaranteed. `N` is the number of threads. If negative it will be the core count divided by `abs(N)`. `M` is the queue depth. If either value is `0` it will be decided based on system configuration. |
|
||||
| cor:N | cosr:N:M with M = 0 |
|
||||
| cor | cosr:N:M with N = 0 and M = 0 |
|
||||
|
||||
@@ -21,3 +21,23 @@ in a directory and possibly some basic metadata about said files. To
|
||||
know details about the files, as one would see from commands like
|
||||
`find` or `ls`, it is required to call `stat` on the file which is
|
||||
controlled by `fuse.getattr`.
|
||||
|
||||
Until Linux v6.16, despite being able to change the size of FUSE
|
||||
messages, `readdir` requests were limited to 1 page (4KiB) in size per
|
||||
message. This limitation limited throughput and therefore performance
|
||||
of the `readdir` call. In v6.16 that was changed to be able to grow to
|
||||
the [fuse-msg-size](fuse-msg-size.md) which allows a lot more data to
|
||||
be sent per request and therefore much better performance in certain
|
||||
situations.
|
||||
|
||||
|
||||
## Technical Details
|
||||
|
||||
* On Linux
|
||||
[getdents](https://man7.org/linux/man-pages/man2/getdents.2.html) is
|
||||
used instead of
|
||||
[readdir](https://man7.org/linux/man-pages/man2/readdir.2.html) in
|
||||
order to leverage larger buffers for better performance on larger
|
||||
directory reads as well as have more control in concurrent policies.
|
||||
* [readdir](https://man7.org/linux/man-pages/man2/readdir.2.html) is
|
||||
used on FreeBSD.
|
||||
|
||||
@@ -5,7 +5,9 @@ The POSIX filesystem API is made up of a number of functions. `creat`,
|
||||
the core functions are grouped into 3 categories: `action`, `create`,
|
||||
and `search`. These functions and categories can be assigned a policy
|
||||
which dictates which branch is chosen when performing that
|
||||
function.
|
||||
function. IE. When an application requests that a file or directory is
|
||||
created how should mergerfs decide which branch to choose to attempt
|
||||
the create? That is the policy.
|
||||
|
||||
Some functions, listed in the category `N/A` below, can not be
|
||||
assigned the normal policies because they are directly related to a
|
||||
@@ -44,9 +46,9 @@ branch base path provided is used. Not the full path to the file or
|
||||
directory in question. Meaning that mounts within the branch will not
|
||||
be considered in the space calculations.
|
||||
|
||||
NOTE: While any policy can be assigned to a function or category, some
|
||||
may not be very useful in practice. For instance: `rand` (random) may
|
||||
be useful for file creation but could lead to very odd behavior if
|
||||
**NOTE:** While any policy can be assigned to a function or category,
|
||||
some may not be very useful in practice. For instance: `rand` (random)
|
||||
may be useful for file creation but could lead to very odd behavior if
|
||||
used for `chmod` if there were more than one copy of the file. Unless
|
||||
users find this flexibility useful it will likely be removed in the
|
||||
future.
|
||||
@@ -69,7 +71,7 @@ whether or not a branch is mounted read-only, and the branch mode
|
||||
[minfreespace](minfreespace.md).
|
||||
|
||||
Policies may have their own additional filtering such as those that
|
||||
require existing paths to be present.
|
||||
require relative paths to be present.
|
||||
|
||||
If all branches are filtered an error will be returned. Typically
|
||||
`EROFS` (read-only filesystem) or `ENOSPC` (no space left on
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# fuse_msg_size
|
||||
# fuse-msg-size
|
||||
|
||||
* `fuse_msg_size=UINT|SIZE`
|
||||
* Defaults to `1M`
|
||||
* Performance improvements often peak at about `4M`
|
||||
* type: `UINT|SIZE`
|
||||
* default: `1M`
|
||||
* example: `fuse-msg-size=1M`
|
||||
* If the value is a `SIZE` in the form of `xB`, `xK`, `xM`, `xG`, or
|
||||
`xT` then it will choose the closest multiple of the system page
|
||||
size
|
||||
* If the value is just an integer without a size multipler suffix then
|
||||
it is treated as a page count
|
||||
`xT` then it will choose the closest multiple of the system page
|
||||
size. If the value is just an integer without a size multipler suffix
|
||||
then it is treated as a page count
|
||||
|
||||
FUSE applications communicate with the kernel over a special character
|
||||
device: `/dev/fuse`. A large portion of the overhead associated with
|
||||
@@ -29,10 +28,10 @@ v6.13](https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/co
|
||||
and above the max value supported by the kernel can range from 1
|
||||
(4KiB) to 65535 (~256MiB) (assuming a page size of 4KiB.) The default
|
||||
used by Linux >= 4.20, and hard coded value used before 4.20, is 32
|
||||
(128KiB). In mergerfs it is referred to as `fuse_msg_size` to make it
|
||||
(128KiB). In mergerfs it is referred to as `fuse-msg-size` to make it
|
||||
clear what it impacts and provide some abstraction.
|
||||
|
||||
If the `fuse_msg_size` value provided is more than the system wide
|
||||
If the `fuse-msg-size` value provided is more than the system wide
|
||||
maximum mergerfs will attempt to increase the system wide value to keep
|
||||
the user from needing to set the value using `sysctl`,
|
||||
`/etc/sysctl.conf`, or `/proc/sys/fs/fuse/max_pages_limit`.
|
||||
@@ -48,4 +47,4 @@ v4.20 and v6.13 the max value is 256. On kernels >= v6.13 the maximum
|
||||
value is 65535.
|
||||
|
||||
NOTE: If you intend to enable `cache.files` you should also set
|
||||
[readahead](readahead.md) to match `fuse_msg_size`.
|
||||
[readahead](readahead.md) to match `fuse-msg-size`.
|
||||
@@ -16,4 +16,4 @@ help. Like [export-support](export-support.md) this is mostly for
|
||||
debugging.
|
||||
|
||||
This option is a kernel mount option so unable to be changed at
|
||||
runtime.
|
||||
runtime. It controls the `default_permissions` FUSE kernel option.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# link_cow
|
||||
# link-cow
|
||||
|
||||
* `link_cow=true|false`
|
||||
* `link-cow=true|false`
|
||||
* Defaults to `false`
|
||||
|
||||
This feature offers similar functionality to what
|
||||
@@ -30,7 +30,7 @@ as appropriate and the original error returned.
|
||||
|
||||
If `moveonenospc` is disabled the underlying error will be returned.
|
||||
|
||||
NOTE: This feature has NO affect on policies. It ONLY applies to the
|
||||
NOTE: This feature has NO effect on policies. It ONLY applies to the
|
||||
literal write function. If the create function returns `ENOSPC` or the
|
||||
[policy returns `ENOSPC`](functions_categories_policies.md#filtering)
|
||||
that error will be returned back to the application making the
|
||||
|
||||
@@ -30,9 +30,11 @@ config file.
|
||||
## mount options
|
||||
|
||||
* **config**: Path to a config file. Same arguments as below in
|
||||
key=val / ini style format.
|
||||
`key` or `key=val` style format. `#` used for comments.
|
||||
* **[branches](branches.md)**: Colon delimited list of branches. Used
|
||||
primarily in config file.
|
||||
primarily in a config file.
|
||||
* **mountpoint=DIRPATH**: The path to the mountpoint. Used primarily
|
||||
in a config file.
|
||||
* **[branches-mount-timeout](branches-mount-timeout.md)=UINT**: Number
|
||||
of seconds to wait at startup for branches to be a mount other than
|
||||
the mountpoint's filesystem. (default: 0)
|
||||
@@ -68,7 +70,7 @@ config file.
|
||||
writable and its mtime or ctime is older than **symlinkify_timeout**
|
||||
files will be reported as symlinks to the original files. Please
|
||||
read more below before using. (default: false)
|
||||
* **[symlinkify_timeout](symlinkify.md)=UINT**: Time to wait, in
|
||||
* **[symlinkify-timeout](symlinkify.md)=UINT**: Time to wait, in
|
||||
seconds, to activate the **symlinkify** behavior. (default: 3600)
|
||||
* **nullrw=BOOL**: Turns reads and writes into no-ops. The request
|
||||
will succeed but do nothing. Useful for benchmarking
|
||||
@@ -86,13 +88,16 @@ config file.
|
||||
* **[export-support](export-support.md)=BOOL**: Sets a low-level FUSE
|
||||
feature intended to indicate the filesystem can support being
|
||||
exported via NFS. (default: true)
|
||||
* **security_capability=BOOL**: If false return ENOATTR when xattr
|
||||
* **[kernel-permissions-check](kernel-permissions-check.md)=BOOL**:
|
||||
Controls the FUSE `default_permissions` option. Primarily for
|
||||
debugging. (default: true)
|
||||
* **security-capability=BOOL**: If false return ENOATTR when xattr
|
||||
security.capability is queried. (default: true)
|
||||
* **[xattr](xattr.md)=passthrough|noattr|nosys**: Runtime control of
|
||||
xattrs. Default is to passthrough xattr requests. 'noattr' will
|
||||
short circuit as if nothing exists. 'nosys' will respond with ENOSYS
|
||||
as if xattrs are not supported or disabled. (default: passthrough)
|
||||
* **[link_cow](link_cow.md)=BOOL**: When enabled if a regular file is
|
||||
* **[link-cow](link-cow.md)=BOOL**: When enabled if a regular file is
|
||||
opened which has a link count > 1 it will copy the file to a
|
||||
temporary file and rename over the original. Breaking the link and
|
||||
providing a basic copy-on-write function similar to
|
||||
@@ -101,7 +106,7 @@ config file.
|
||||
means it will always use all branches in statfs calculations. 'full'
|
||||
is in effect path preserving and only includes branches where the
|
||||
path exists. (default: base)
|
||||
* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to
|
||||
* **statfs-ignore=none|ro|nc**: 'ro' will cause statfs calculations to
|
||||
ignore available space for branches mounted or tagged as 'read-only'
|
||||
or 'no create'. 'nc' will ignore available space for branches tagged
|
||||
as 'no create'. (default: none)
|
||||
@@ -118,20 +123,20 @@ config file.
|
||||
directory and symlink to it.
|
||||
* **[readahead](readahead.md)=UINT**: Set readahead (in kilobytes) for
|
||||
mergerfs and branches if greater than 0. (default: 0)
|
||||
* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel
|
||||
* **posix-acl=BOOL**: Enable POSIX ACL support (if supported by kernel
|
||||
and underlying filesystem). (default: false)
|
||||
* **async_read=BOOL**: Perform reads asynchronously. If disabled or
|
||||
* **async-read=BOOL**: Perform reads asynchronously. If disabled or
|
||||
unavailable the kernel will ensure there is at most one pending read
|
||||
request per file handle and will attempt to order requests by
|
||||
offset. (default: true)
|
||||
* **[fuse_msg_size](fuse_msg_size.md)=PAGESIZE**: Set the max number of
|
||||
* **[fuse-msg-size](fuse-msg-size.md)=PAGESIZE**: Set the max number of
|
||||
pages per FUSE message. Only available on Linux >= 4.20 and ignored
|
||||
otherwise. (min: 1; max: 65535; default: "1M")
|
||||
* **[threads](threads.md)=INT**: Number of threads to use. When used
|
||||
alone (`process-thread-count=-1`) it sets the number of threads
|
||||
reading and processing FUSE messages. When used together it sets the
|
||||
number of threads reading from FUSE. When set to zero it will
|
||||
attempt to discover and use the number of logical cores. If the
|
||||
* **[read-thread-count](threads.md)=INT**: Number of threads to
|
||||
use. When used alone (`process-thread-count=-1`) it sets the number
|
||||
of threads reading and processing FUSE messages. When used together
|
||||
it sets the number of threads reading from FUSE. When set to zero it
|
||||
will attempt to discover and use the number of logical cores. If the
|
||||
thread count is set negative it will look up the number of cores
|
||||
then divide by the absolute value. ie. threads=-2 on an 8 core
|
||||
machine will result in 8 / 2 = 4 threads. There will always be at
|
||||
@@ -139,7 +144,6 @@ config file.
|
||||
`process-thread-count` then it will try to pick reasonable values
|
||||
based on CPU thread count. NOTE: higher number of threads increases
|
||||
parallelism but usually decreases throughput. (default: 0)
|
||||
* **[read-thread-count](threads.md)=INT**: Alias for `threads`.
|
||||
* **[process-thread-count](threads.md)=INT**: Enables separate thread
|
||||
pool to asynchronously process FUSE requests. In this mode
|
||||
`read-thread-count` refers to the number of threads reading FUSE
|
||||
@@ -150,7 +154,7 @@ config file.
|
||||
time. Meaning the total memory usage of the queues is queue depth
|
||||
multiplied by the number of process threads plus read thread
|
||||
count. 0 sets the depth to the same as the process thread
|
||||
count. (default: 0)
|
||||
count. (default: 2)
|
||||
* **[pin-threads](pin-threads.md)=STR**: Selects a strategy to pin
|
||||
threads to CPUs (default: unset)
|
||||
* **[flush-on-close](flush-on-close.md)=never|always|opened-for-write**:
|
||||
@@ -159,15 +163,17 @@ config file.
|
||||
* **scheduling-priority=INT**: Set mergerfs' scheduling
|
||||
priority. Valid values range from -20 to 19. See `setpriority` man
|
||||
page for more details. (default: -10)
|
||||
* **[proxy-ioprio](proxy-ioprio.md)=BOOL**: Use ioprio value from
|
||||
calling threads when reading and writing. (default: false)
|
||||
* **fsname=STR**: Sets the name of the filesystem as seen in
|
||||
**mount**, **df**, etc. Defaults to a list of the source paths
|
||||
concatenated together with the longest common prefix removed.
|
||||
* **[func.FUNC](functions_categories_policies.md)=POLICY**: Sets the
|
||||
specific FUSE function's policy. See below for the list of value
|
||||
types. Example: **func.getattr=newest**
|
||||
* **[func.readdir](func_readdir.md)=seq|cosr|cor|cosr:INT|cor:INT**: Sets `readdir`
|
||||
policy. INT value sets the number of threads to use for
|
||||
concurrency. (default: seq)
|
||||
* **[func.readdir](func_readdir.md)=seq|cosr|cor|cosr:INT|cor:INT**:
|
||||
Sets `readdir` policy. INT value sets the number of threads to use
|
||||
for concurrency. (default: seq)
|
||||
* **[category.action](functions_categories_policies.md)=POLICY**: Sets
|
||||
policy of all FUSE functions in the action category. (default:
|
||||
epall)
|
||||
@@ -182,7 +188,7 @@ config file.
|
||||
timeout in seconds. (default: 1)
|
||||
* **[cache.entry](cache.md#cacheentry)=UINT**: File name lookup cache
|
||||
timeout in seconds. (default: 1)
|
||||
* **[cache.negative_entry](cache.md#cachenegative_entry)=UINT**:
|
||||
* **[cache.negative-entry](cache.md#cachenegative-entry)=UINT**:
|
||||
Negative file name lookup cache timeout in seconds. (default: 0)
|
||||
* **[cache.files](cache.md#cachefiles)=off|partial|full|auto-full|per-process**:
|
||||
File page caching mode (default: off)
|
||||
@@ -201,15 +207,23 @@ config file.
|
||||
with `cache.files=per-process` (if the process is not in
|
||||
`process-names`) or `cache.files=off`. (Is a kernel feature added in
|
||||
v6.2) (default: true)
|
||||
* **[passthrough](passthrough.md)**: Enable [FUSE IO
|
||||
* **[passthrough.io](passthrough.md)=ENUM**: Enable [FUSE IO
|
||||
passthrough](https://kernelnewbies.org/Linux_6.9#Faster_FUSE_I.2FO)
|
||||
if available. (default: off)
|
||||
* **gid-cache-expire-timeout**: Number of seconds till supplemental
|
||||
group data is refreshed in the [GID
|
||||
* **passthrough.max-stack-depth=INT**: Set to `1` another filesystem
|
||||
can be stacked on mergerfs. Set to `2` to have mergerfs stacked over
|
||||
another filesystem. (default: 1)
|
||||
* **gid-cache.expire-timeout=INT**: Number of seconds till
|
||||
supplemental group data is refreshed in the [GID
|
||||
cache](../known_issues_bugs.md#supplemental-user-groups). (default:
|
||||
3600)
|
||||
* **gid-cache-remove-timeout**: Number of seconds to wait till cached
|
||||
data is removed due to lack of usage. (default: 43200)
|
||||
* **gid-cache.remove-timeout=INT**: Number of seconds to wait till
|
||||
cached data is removed due to lack of usage. (default: 43200)
|
||||
* **remember-nodes=INT**: The number of seconds to keep the internal
|
||||
representation of a file once the OS tells mergerfs it is no longer
|
||||
needed. Really only needed for [exporting mergerfs via
|
||||
NFS](../remote_filesystems.md) (default: 0)
|
||||
* **noforget**: Effectively sets `remember-nodes` to infinity.
|
||||
|
||||
**NOTE:** Options are evaluated in the order listed so if the options
|
||||
are **func.rmdir=rand,category.action=ff** the **action** category
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# passthrough
|
||||
# passthrough.io
|
||||
|
||||
* default: `off`
|
||||
* arguments:
|
||||
@@ -14,7 +14,7 @@ to act as an active proxy for all read and write requests. This
|
||||
results in, at times, significant overhead compared to direct
|
||||
interaction with the underlying filesystems. Not because `mergerfs` is
|
||||
doing anything particularly slow but because the additional
|
||||
communication and data transfers required. With the `passthrough`
|
||||
communication and data transfers required. With the `passthrough.io`
|
||||
feature `mergerfs` is able to instruct the kernel to perform reads and
|
||||
writes directly on the underlying file. Bypassing `mergerfs` entirely
|
||||
(specifically and only for reads and writes) and thereby providing
|
||||
@@ -31,54 +31,53 @@ features related to read/write are affected.
|
||||
* [readahead](readahead.md): Still affects the `readahead` values of
|
||||
underlying filesystems and `mergerfs` itself but no longer relevant
|
||||
to `mergerfs` in that it is not servicing IO requests.
|
||||
* [fuse_msg_size](fuse_msg_size.md): The primary reason to increase
|
||||
* [fuse-msg-size](fuse-msg-size.md): The primary reason to increase
|
||||
the FUSE message size is to allow transferring more data per request
|
||||
which helps improve read and write performance. Without read/write
|
||||
requests being sent to `mergerfs` there is little reason to have
|
||||
larger message sizes since the only other message larger than 1
|
||||
page, directory reading, currently has a small fixed buffer size.
|
||||
* parallel-direct-writes: Not only is `direct-io` and `passthrough`
|
||||
* parallel-direct-writes: Not only is `direct-io` and `passthrough.io`
|
||||
mutually exclusive but since `mergerfs` no longer receives write
|
||||
requests in passthrough mode then there is no parallel writes
|
||||
possible.
|
||||
* [cache.writeback](cache.md): FUSE's writeback caching is
|
||||
incompatible with `passthrough`. If `cache.writeback=true` when
|
||||
enabling `passthrough` it will be reset to `false`.
|
||||
* [cache.files](cache.md): Must be enabled for `passthrough` to
|
||||
incompatible with `passthrough.io`. If `cache.writeback=true` when
|
||||
enabling `passthrough.io` it will be reset to `false`.
|
||||
* [cache.files](cache.md): Must be enabled for `passthrough.io` to
|
||||
work. When `cache.files=off` FUSE's `direct-io` mode is enabled
|
||||
which overrides `passthrough.` Meaning `cache.files` should be set
|
||||
to `partial`, `full` or `auto-full` to use `passthrough`. If
|
||||
`cache.files` is set to `off` when using `passthrough` it will
|
||||
which overrides `passthrough.io`. Meaning `cache.files` should be
|
||||
set to `partial`, `full` or `auto-full` to use `passthrough.io`. If
|
||||
`cache.files` is set to `off` when using `passthrough.io` it will
|
||||
reset it to `auto-full`.
|
||||
|
||||
Technically `mergerfs` has the ability to choose options like
|
||||
`passthrough`, `direct-io`, and page caching independently for every
|
||||
file opened. However, at the moment there is no use case for picking
|
||||
and choosing which to enable outside `cache.files=per-process` (which
|
||||
is largely unnecessary on Linux v6.6 and above. See
|
||||
`passthrough.io`, `direct-io`, and page caching independently for
|
||||
every file opened. However, at the moment there is no use case for
|
||||
picking and choosing which to enable outside `cache.files=per-process`
|
||||
(which is largely unnecessary on Linux v6.6 and above. See
|
||||
[direct-io-allow-mmap](options.md)) If such a use case arises please
|
||||
[reach out to the author](../support.md) to discuss.
|
||||
|
||||
Unlike [preload.so](../tooling.md#preloadso), `passthrough` will work for
|
||||
any software interacting with `mergerfs`. However, `passthrough`
|
||||
requires Linux v6.9 or above to work.
|
||||
Unlike [preload.so](../tooling.md#preloadso), `passthrough.io` will
|
||||
work for any software interacting with `mergerfs`. However,
|
||||
`passthrough.io` requires Linux v6.9 or above to work.
|
||||
|
||||
**NOTE:** This feature will **ONLY** work if `mergerfs` is running as
|
||||
`root` as currently only `root` is allowed to leverage the kernel
|
||||
feature.
|
||||
|
||||
**NOTE:** If a file has been opened and `passthrough` enabled, while
|
||||
that file is open, if another open request is made `mergerfs` must
|
||||
also enable `passthrough` for the second open request. This is a
|
||||
limitation of how the passthrough feature works. Though there is no
|
||||
known usecase where this is useful.
|
||||
**NOTE:** If a file has been opened and `passthrough.io` enabled,
|
||||
while that file is open, if another open request is made `mergerfs`
|
||||
must also enable `passthrough.io` for the second open request. This is
|
||||
a limitation of how the passthrough feature works.
|
||||
|
||||
**NOTE:** In order to add `passthrough` feature to `mergerfs` it was
|
||||
necessary to remove the "feature" where mergerfs could open the same
|
||||
file on different branches. Such as using `func.open=rand` and having
|
||||
multiple files at the same relative path across different
|
||||
**NOTE:** In order to add `passthrough.io` feature to `mergerfs` it
|
||||
was necessary to remove the "feature" where mergerfs could open the
|
||||
same file on different branches. Such as using `func.open=rand` and
|
||||
having multiple files at the same relative path across different
|
||||
branches. This "feature" was very very rarely used and it was
|
||||
impossible to support `passthrough` without changing the behavior.
|
||||
impossible to support `passthrough.io` without changing that behavior.
|
||||
|
||||
|
||||
## Alternatives
|
||||
@@ -104,7 +103,7 @@ Summary: passthrough is ~95% native speed. 200% faster than
|
||||
|
||||
* straight to tmpfs: 1.7 GB/s
|
||||
* nullrw=true: 2.1 GB/s
|
||||
* cache.files=off; passthrough=off: 800 MB/s
|
||||
* cache.files=auto-full; passthrough=rw: 1.6 GB/s
|
||||
* cache.files=off; passthrough.io=off: 800 MB/s
|
||||
* cache.files=auto-full; passthrough.io=rw: 1.6 GB/s
|
||||
* cache.files=auto-full; cache.writeback=false: 518 MB/s
|
||||
* cache.files=auto-full; cache.writeback=true: 518 MB/s
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# proxy-ioprio
|
||||
|
||||
* type: `BOOL`
|
||||
* default: `false`
|
||||
* example: `proxy-ioprio=true`
|
||||
|
||||
In Linux certain process schedulers have the ability to
|
||||
[prioritize](https://man7.org/linux/man-pages/man2/ioprio_set.2.html)
|
||||
based on a per thread [IO
|
||||
priority](https://www.kernel.org/doc/Documentation/block/ioprio.txt)
|
||||
assigned to the software by users using tools such as
|
||||
[ionice](https://man7.org/linux/man-pages/man1/ionice.1.html). Since
|
||||
such details are not provided by the FUSE protocol, users may not use
|
||||
schedulers which support IO priority, and the extra overhead to query
|
||||
the priority and set it within mergerfs it does not by default attempt
|
||||
to mirror/proxy the value. When enabled however, for all read and
|
||||
write requests, mergerfs will query the priority of the requesting
|
||||
process/thread and set the same value on the thread within mergerfs
|
||||
making the read/write.
|
||||
|
||||
This may be useful in situation where the system has high IO load or
|
||||
processes are known to have varying priorities. See the [man
|
||||
page](https://man7.org/linux/man-pages/man2/ioprio_set.2.html) for
|
||||
specific details.
|
||||
|
||||
Keep in mind that if no IO scheduler has been set for a thread then by
|
||||
default the IO priority will match the CPU nice value which for
|
||||
mergerfs is set by [scheduling-priority](options.md). [Nice
|
||||
value](https://man7.org/linux/man-pages/man2/getpriority.2.html)
|
||||
proxying may be added in a future release.
|
||||
|
||||
|
||||
## Conflicts With Other Options
|
||||
|
||||
If using [IO passthrough](passthrough.md) there is no reason to set
|
||||
this value to `true`. However, since `passthrough.io` leads to
|
||||
mergerfs IO calls being bypassed entirely will have no impact on
|
||||
performance or behavior regardless.
|
||||
|
||||
When using `passthrough.io` the IO priority of the client app would be
|
||||
used directly given the IO is passed through.
|
||||
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
Only Linux. This is not supported on FreeBSD.
|
||||
|
||||
|
||||
## Performance Impact
|
||||
|
||||
Despite the additional syscalls requires the impact appears
|
||||
undetectable with larger buffer sizes. At very small buffer sizes
|
||||
(such as 512 bytes) there is a noticable but small impact (~1.5%).
|
||||
@@ -6,7 +6,7 @@ value unit is in kibibytes.
|
||||
* `readahead=1024`
|
||||
|
||||
While the max size of messages sent between the kernel and mergerfs is
|
||||
configurable via the [fuse_msg_size](fuse_msg_size.md) option that
|
||||
configurable via the [fuse-msg-size](fuse-msg-size.md) option that
|
||||
doesn't mean that is the size used by the kernel for read and
|
||||
writes.
|
||||
|
||||
|
||||
@@ -55,6 +55,6 @@ returned but it will still be possible.
|
||||
|
||||
## Additional Reading
|
||||
|
||||
* [Do hardlinks work?](../faq/technical_behavior_and_limitations.md#do-hardlinks-work)
|
||||
* [Do hardlinks work?](../faq/technical_behavior_and_limitations.md#do-hard-links-work)
|
||||
* [How does mergerfs handle moving and copying of files?](../faq/technical_behavior_and_limitations.md#how-does-mergerfs-handle-moving-and-copying-of-files)
|
||||
* [Why are file moves and renames failing?](../faq/why_isnt_it_working.md#why-are-file-moves-renames-linkshardlinks-failing)
|
||||
* [Why are file moves and renames failing?](../faq/why_isnt_it_working.md#why-are-file-moves-renames-linkshard-links-failing)
|
||||
|
||||
@@ -68,6 +68,11 @@ passthrough IO or other features.
|
||||
|
||||
### time based expiring
|
||||
|
||||
**NOTE:** this script is an **example**, not particularly efficient, and
|
||||
really not intended for production deployments. Until such time that
|
||||
mergerfs provides tooling for this usecase consider using
|
||||
[mergerfs-cache-mover](https://github.com/monstermuffin/mergerfs-cache-mover).
|
||||
|
||||
Move files from cache filesystem to base pool which have an access
|
||||
time older than the supplied number of days. Replace `-atime` with
|
||||
`-amin` in the script if you want minutes rather than days.
|
||||
@@ -99,6 +104,12 @@ respectively.
|
||||
|
||||
### percentage full expiring
|
||||
|
||||
**NOTE:** this script is an **example**, not particularly efficient, and
|
||||
really not intended for production deployments. Until such time that
|
||||
mergerfs provides tooling for this usecase consider using
|
||||
[mergerfs-cache-mover](https://github.com/monstermuffin/mergerfs-cache-mover).
|
||||
|
||||
|
||||
While the cache filesystem's percentage full is above the provided
|
||||
value move the oldest file from the cache filesystem to the base pool.
|
||||
|
||||
|
||||
@@ -18,37 +18,71 @@ mergerfs [does not impact](usage_and_functionality.md) the underlying
|
||||
filesystems and can be added or removed without any impact it is
|
||||
extremely easy to test and experiment with different settings.
|
||||
|
||||
Additional reading:
|
||||
|
||||
* [Config/Options](../config/options.md)
|
||||
|
||||
|
||||
## What create policy should I use?
|
||||
|
||||
If it isn't clear to you then you should use `pfrd` as described in
|
||||
the [quickstart guide](../quickstart.md).
|
||||
|
||||
|
||||
## Why is pfrd the default create policy?
|
||||
|
||||
Originally the default was `epmfs` however it was found to cause
|
||||
significant confusion among new users who expected mergerfs to always
|
||||
choose a branch with available space. For mergerfs v2.41.0 it was
|
||||
decided to change the default to the less restrictive policy `pfrd`.
|
||||
significant confusion among new users who assumed mergerfs to behave
|
||||
like other software which always choose a branch with most available
|
||||
space without any filtering or restrictions. The reason for using
|
||||
`epmfs` was to allow people to use mergerfs without impacting the
|
||||
layout of their data across the branches. Keeping the total entropy of
|
||||
the system in check. This, however, was not understood by users who
|
||||
did not read the documentation and caused significant support burdens.
|
||||
|
||||
For mergerfs v2.41.0 it was decided to change the default to `pfrd` to
|
||||
minimize the support burden.
|
||||
|
||||
`pfrd` was chosen because it prioritizes placement to branches based
|
||||
on free space (percentage wise) without overloading a specific branch
|
||||
as `mfs`, `lus`, or other policies could when a singular branch has
|
||||
significantly more free space. Since it is impossible to know the
|
||||
usage and access patterns of the filesystem spreading out files across
|
||||
the branches also spreads the load and therefore good for general use
|
||||
cases making it a good default.
|
||||
|
||||
Additional reading:
|
||||
|
||||
* [functions, categories and policies](../config/functions_categories_policies.md)
|
||||
|
||||
|
||||
## How can I ensure files are collocated on the same branch?
|
||||
|
||||
Many people like the idea of ensuring related files, such as all the
|
||||
files to a TV show season or songs in an album, are stored on the same
|
||||
storage device. However, most people have no actual need for this
|
||||
storage device. However, most people have no technical need for this
|
||||
behavior.
|
||||
|
||||
1. If you backup your data it is extremely likely your backup solution
|
||||
can restore only those files you are missing.
|
||||
2. Software such as **Sonarr** can manage the downloading and post
|
||||
processing of bespoke episodes which may be missing in a
|
||||
season. Either by downloading the episode individually if available
|
||||
or by downloading a full season.
|
||||
can restore only those files you are missing. There is no need to
|
||||
store data in related units to ensure restoring data is easy.
|
||||
2. Software such as [Sonarr](https://sonarr.tv) can manage the
|
||||
downloading and post processing of bespoke files / episodes which
|
||||
may be missing. Either by downloading the episode individually if
|
||||
available or by downloading a full season.
|
||||
3. There is no benefit to keeping files collocated with regard to
|
||||
drive spinup, caching, or other secondary concern.
|
||||
drive spinup, caching, or other secondary concern. In fact
|
||||
depending on usage patterns placing the data on the same underlying
|
||||
device could harm performance.
|
||||
|
||||
The main use case for wanting collocation is where the branch is going
|
||||
to be removed from the pool and you wish to have all data from some
|
||||
logical set on that device. Such as you intend to take a drive out of
|
||||
the pool to take on a trip and want a whole show on the
|
||||
drive. However, even in these situations you typically end up needing
|
||||
to curate the files anyway because it has show A but not show B.
|
||||
to be removed from the pool and used in isolation temporarily and you
|
||||
wish to have all data from some logical set on that device. Such as
|
||||
you intend to take a drive out of the pool to take on a trip and want
|
||||
a whole show or artist's works on the drive. However, even in these
|
||||
situations you typically end up needing to curate the files anyway
|
||||
because it has show A but not show B. In such cases it is more
|
||||
convient to use a separate devices and sync what you wish to it.
|
||||
|
||||
All that said you can accomplish collocation to varying degrees using
|
||||
the following methods:
|
||||
@@ -56,33 +90,37 @@ the following methods:
|
||||
1. Use
|
||||
[mergerfs.consolidate](https://github.com/trapexit/mergerfs-tools/blob/master/src/mergerfs.consolidate)
|
||||
when consolidation is needed.
|
||||
2. Use a `msp` create policy.
|
||||
2. Use a `msp` create policy to do a best effort collocation without
|
||||
needing explicit directory curation.
|
||||
3. Use `epmfs` or other `ep` create policy and manually create paths
|
||||
on the branches directly.
|
||||
4. Use a `ep` `create` policy and `rand` for `mkdir`.
|
||||
on the branches directly as you need/desire.
|
||||
4. Use a `ep` `create` policy and `pfrd` or `rand` for `mkdir`.
|
||||
|
||||
|
||||
## How can I balance files across the pool?
|
||||
|
||||
Similar to collocation there is generally little reason to balance
|
||||
files.
|
||||
files across the pool branches.
|
||||
|
||||
1. Since prediction of a filesystem's death or loss of data is near
|
||||
impossible there is little reason to balance in hopes of limiting
|
||||
data loss.
|
||||
1. Since prediction of a device's/filesystem's death or loss of data
|
||||
is near impossible there is little reason to balance in hopes of
|
||||
limiting data loss.
|
||||
2. While performance could be impacted by having too much reading or
|
||||
writing happen to singular underlying filesystems balancing won't
|
||||
help unless you have the ability to manage the access patterns to
|
||||
the pool.
|
||||
3. Over time most configurations will lead to a random distribution of
|
||||
files across the branches which is effectively "balancing."
|
||||
3. Over time the churn of adding and removing files will lead to a
|
||||
random distribution of files across the branches which is
|
||||
effectively "balancing" if using create policies which do not favor
|
||||
any particular branch.
|
||||
|
||||
If you wish to move files around or balance the pool you can:
|
||||
All that said if you wish to move files around or balance the pool you
|
||||
can:
|
||||
|
||||
1. Use `rand` or `pfrd` create policies and just use your system as
|
||||
normal.
|
||||
2. Write simple scripts using rsync or similar to move files around as
|
||||
you wish.
|
||||
2. Write scripts using rsync or similar to move files around as you
|
||||
wish.
|
||||
3. Use
|
||||
[mergerfs.balance](https://github.com/trapexit/mergerfs-tools/blob/master/src/mergerfs.balance). Keep
|
||||
in mind that this tool is really just an example of how to
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
## What should mergerfs NOT be used for?
|
||||
|
||||
* Situations where you need large amounts of contiguous space beyond
|
||||
that available on any singular device. Such as putting 10GiB file on
|
||||
2 6GiB filesystems.
|
||||
* databases: Even if the database stored data in separate files
|
||||
(mergerfs wouldn't offer much otherwise) the higher latency of the
|
||||
indirection will really harm performance. If it is a lightly used
|
||||
@@ -9,13 +12,13 @@
|
||||
* VM images: For the same reasons as databases. VM images are accessed
|
||||
very aggressively and mergerfs will introduce a lot of extra latency.
|
||||
* As replacement for RAID: mergerfs is just for pooling branches. If
|
||||
you need that kind of device performance aggregation or high
|
||||
availability you should stick with RAID. However, it is fine to put
|
||||
a filesystem which is on a RAID setup in mergerfs.
|
||||
you need device performance aggregation or high availability you
|
||||
should stick with RAID. However, it is fine to put a filesystem
|
||||
which is on a RAID setup in mergerfs.
|
||||
|
||||
**However, if using [passthrough](../config/passthrough.md) the above
|
||||
situations are less likely to be a concern. Best to do testing for
|
||||
your specific use case.**
|
||||
**However, if using [passthrough](../config/passthrough.md) the
|
||||
performance related issues above are less likely to be a concern. Best
|
||||
to do testing for your specific use case.**
|
||||
|
||||
|
||||
## It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
|
||||
|
||||
@@ -1,26 +1,71 @@
|
||||
# Technical Behavior and Limitations
|
||||
|
||||
## Do hardlinks work?
|
||||
## Do hard links work?
|
||||
|
||||
Yes. There is no option to enable or disable links (or renames.) They
|
||||
are fundamentally supported in compatible situations. That said the
|
||||
inode of a file is not necessarily indicative of two file names
|
||||
linking to the same underlying data. See also the option `inodecalc`
|
||||
for how inode values are calculated.
|
||||
Yes. [links](https://en.wikipedia.org/wiki/Hard_link) (otherwise known
|
||||
as hard links) are fundamentally supported by mergerfs. There is no
|
||||
option to enable or disable links. All comments elswhere claiming they
|
||||
do not work are related to the setup of the user's system.
|
||||
|
||||
What mergerfs does not do is fake hard links across branches. Read the
|
||||
section [rename & link](../config/rename_and_link.md) for how it
|
||||
works.
|
||||
See the [Intro to Filesystems](../intro_to_filesystems.md) page for more
|
||||
details on what a `link` is.
|
||||
|
||||
Remember that hardlinks will **NOT** work across devices. That
|
||||
includes between the original filesystem and a mergerfs pool, between
|
||||
two separate pools of the same underlying filesystems, or bind mounts
|
||||
of paths within the mergerfs pool. The latter is common when using
|
||||
Docker or Podman. Multiple volumes (bind mounts) to the same
|
||||
underlying filesystem are considered different devices. There is no
|
||||
way to link or rename between them. You should mount in the highest
|
||||
directory in the mergerfs pool that includes all the paths you need if
|
||||
you want links and rename to work.
|
||||
A limitation of a `link` is that it **must** reference a file/inode
|
||||
within the same filesystem or device. When a request is made to the
|
||||
operating system to `link` (or `rename`) a file to a target location
|
||||
the OS will first check if the source and target are the same
|
||||
filesystem / device. If not it will return the error `EXDEV` meaning
|
||||
that the request is not able to be made due to the result being "cross
|
||||
device".
|
||||
|
||||
What catches many users off guard is that this "cross device" check is
|
||||
not very deep or involved. Even the same filesystem bind mounted to
|
||||
another location will typically be considered a different device. So
|
||||
when mergerfs is creating a union of other filesystems there is no way
|
||||
to link or rename from one of the underlying branches into
|
||||
mergerfs. They are different filesystems even if logically they seem
|
||||
related or the same. And similarly it is impossible for mergerfs to
|
||||
truly make a link between two branches which are not in fact the same
|
||||
mounted filesystem.
|
||||
|
||||
This is relevant to mergerfs users because bind mounts are very common
|
||||
when using container runtimes such as Docker, Podman, or
|
||||
Kubernetes. When you add a volume or mount to your container that is
|
||||
in fact a bind mount of that path. Meaning if you were to add
|
||||
`/mnt/mergerfs/downloads` and `/mnt/mergerfs/movies` as separate
|
||||
mounts into your container any request to link between the two
|
||||
locations within the container will fail with the `EXDEV` error even
|
||||
though it will work perfectly fine on the host. If you wish for `link`
|
||||
and `rename` to work you **MUST** mount into the container a point in
|
||||
the directory structure above what you wish to use. In this case
|
||||
`/mnt/mergerfs`.
|
||||
|
||||
Another situation where people will hit unexpected `EXDEV` errors is
|
||||
when they use a policy which makes file creation decisions based on
|
||||
existing paths. If the source of a `link` or `rename` is on branch A
|
||||
but the target directory is on branch B, given the whole point of the
|
||||
policy is to strictly enforce behavior based on what branches already
|
||||
exist, it must return `EXDEV`. Doing otherwise would fundamentally
|
||||
break the purpose of the policy. Read [rename &
|
||||
link](../config/rename_and_link.md) for more details on how mergerfs
|
||||
handles those two functions.
|
||||
|
||||
When in doubt it is trivial to test `link`:
|
||||
|
||||
```shell
|
||||
$ cd /mnt/mergerfs/
|
||||
$ touch foo
|
||||
$ ln -v foo bar
|
||||
'bar' => 'foo'
|
||||
$ stat foo bar | grep Inode
|
||||
Device: 0,173 Inode: 123456 Links: 2
|
||||
Device: 0,173 Inode: 123456 Links: 2
|
||||
```
|
||||
|
||||
For the more technical: within mergerfs the inode of a file is not
|
||||
necessarily indicative of two file names linking to the same
|
||||
underlying data. For more details as to why that is see the docs on
|
||||
[inodecalc](../config/inodecalc.md).
|
||||
|
||||
|
||||
## Do reflink, FICLONE, or FICLONERANGE work?
|
||||
@@ -201,3 +246,39 @@ contention and therefore lower performance than Linux.
|
||||
Additionally, mergerfs [utilizes a cache for supplemental
|
||||
groups](../known_issues_bugs.md#supplemental-user-groups) due the the
|
||||
high cost of querying that information.
|
||||
|
||||
|
||||
## Does mergerfs support idmap?
|
||||
|
||||
Yes. At least in so far as it's been enabled now the FUSE itself
|
||||
allows a filesystem to indicate it is allowed.
|
||||
|
||||
Requires that
|
||||
[kernel-permissions-check](../config/kernel-permissions-check.md) be
|
||||
enabled (the default.)
|
||||
|
||||
If there are any usage issues contact the [author](../support.md).
|
||||
|
||||
|
||||
## What happens if a branch filesystem blocks?
|
||||
|
||||
POSIX filesystem calls unfortunately are entirely synchronous. If the
|
||||
underlying filesystem mergerfs is interacting with freezes up or
|
||||
blocks then the thread issuing the request will block same as any
|
||||
other piece of software. If enough threads block then mergerfs will
|
||||
block. There are no timeouts or ways to truly work around this
|
||||
situation. If mergerfs spawned new threads it is very likely they too
|
||||
would end up blocked.
|
||||
|
||||
|
||||
## What information does mergerfs have when making decisions?
|
||||
|
||||
Every incoming request contains:
|
||||
|
||||
* The `tid` or `pid` of the calling thread/process
|
||||
* The `uid` and `gid` of the calling thread/process
|
||||
* The `umask` of the calling thread/process
|
||||
* The arguments of the filesystem call in question
|
||||
|
||||
Naturally the mergerfs config as well as anything queriable from the
|
||||
operating system or filesystems are also available.
|
||||
|
||||
@@ -105,7 +105,7 @@ normal conditions.
|
||||
|
||||
Keep in mind that if out-of-band changes are made while also
|
||||
leveraging any caching of the filesystem layout such as with
|
||||
`cache.entry`, `cache.negative_entry`, `cache.attr`, `cache.symlinks`,
|
||||
`cache.entry`, `cache.negative-entry`, `cache.attr`, `cache.symlinks`,
|
||||
or `cache.readdir` you may experience temporary inconsistency till the
|
||||
cache expires. mergerfs is not actively watching all branches for
|
||||
changes and the kernel will have no way to know anything changed so as
|
||||
|
||||
@@ -36,15 +36,15 @@ and [FAQ](configuration_and_policies.md) for policy recommendations.
|
||||
|
||||
## Why do I get an "out of space" / "no space left on device" / ENOSPC error even though there appears to be lots of space available?
|
||||
|
||||
One or multiple of the below are likely causing branches
|
||||
One or multiple of the below are likely causing branches to be
|
||||
[filtered out](../config/functions_categories_policies.md#filtering).
|
||||
|
||||
* [minfreespace](../config/minfreespace.md) could be filtering out
|
||||
branches
|
||||
* if using an `ep*`
|
||||
[policy](../config/functions_categories_policies.md) there may not
|
||||
be an available branch
|
||||
* due to [branch modes](../config/branches.md#branch-mode) being set
|
||||
to to `NC` or `RO` branches will be excluded from consideration
|
||||
* using an `ep*` [policy](../config/functions_categories_policies.md)
|
||||
and there may not be an available branch
|
||||
* [branch modes](../config/branches.md#branch-mode) set to `NC` or
|
||||
`RO` branches will be excluded from consideration
|
||||
* if using `ext4` ensure you are not running into reserve limits (use
|
||||
`tune2fs -m 0` to remove the reserve)
|
||||
* it is possible that the filesystem selected by the policy has run
|
||||
@@ -56,7 +56,7 @@ within the pool (behavior modified by [statfs](../config/statfs.md)
|
||||
and [statfs_ignore](../config/statfs.md#statfs_ignore)). It does not
|
||||
represent a contiguous space. In the same way that read-only
|
||||
filesystems, those with quotas, or reserved space report the full
|
||||
theoretical space available. Not the practical usable space.
|
||||
theoretical space available but not the practical usable space.
|
||||
|
||||
|
||||
## Why isn't the create policy working?
|
||||
@@ -134,7 +134,7 @@ the use of mergerfs from [container platforms such as
|
||||
Docker.](compatibility_and_integration.md)
|
||||
|
||||
|
||||
## Why are file moves, renames, links/hardlinks failing?
|
||||
## Why are file moves, renames, links/hard links failing?
|
||||
|
||||
mergerfs fully supports rename and link functions to the degree that
|
||||
is possible in a union filesystem. Problems related to rename and link
|
||||
@@ -225,18 +225,34 @@ the software's author and request proper handling of `EXDEV` errors.
|
||||
### Additional Reading
|
||||
|
||||
* [rename and link](../config/rename_and_link.md)
|
||||
* [Do hardlinks work?](technical_behavior_and_limitations.md#do-hardlinks-work)
|
||||
* [Do hard links work?](technical_behavior_and_limitations.md#do-hard-links-work)
|
||||
* [How does mergerfs handle moving and copying of files?](technical_behavior_and_limitations.md#how-does-mergerfs-handle-moving-and-copying-of-files)
|
||||
* [Moving files and directories fails with Samba](../known_issues_bugs.md#moving-files-and-directories-fails-with-samba)
|
||||
|
||||
|
||||
## Why is rtorrent failing with ENODEV (No such device)?
|
||||
## Why is software needing mmap failing?
|
||||
|
||||
Be sure to set
|
||||
[cache.files=partial|full|auto-full|per-process](../config/cache.md)
|
||||
or use Linux kernel v6.6 or above. rtorrent and some other
|
||||
applications use [mmap](http://linux.die.net/man/2/mmap) to read and
|
||||
write to files and offer no fallback to traditional methods.
|
||||
See [Known Issues and Bugs#3rd Party
|
||||
Software](../known_issues_bugs.md#3rd-party-software) and
|
||||
[QuickStart](../quickstart.md).
|
||||
|
||||
|
||||
## Why don't quick scans fail to pick up new media?
|
||||
|
||||
Media consumption software like Plex, Jellyfin, Emby, Airsonic,
|
||||
Navidrome, etc. will use timestamps of directories to see if it should
|
||||
be scanned. Be sure to set `func.getattr=newest` to catch that.
|
||||
|
||||
A full scan would still pick up all media and for those concerned
|
||||
about device activity it is recommened to disable quick scans and
|
||||
leave periodic full scans so the activity is managed. Or to configure
|
||||
your software which manages downloads (such as *Arr family of
|
||||
software) to trigger specific scans when files are added to the pool.
|
||||
|
||||
Additional reading:
|
||||
|
||||
* [Does inotify and fanotify
|
||||
work?](compatibility_and_integration.md#does-inotify-and-fanotify-work)
|
||||
|
||||
|
||||
## Why don't I see mergerfs options in mount command or /proc/mounts?
|
||||
|
||||
+25
-6
@@ -5,7 +5,10 @@
|
||||
[union filesystem](https://en.wikipedia.org/wiki/Union_mount) geared
|
||||
towards simplifying storage and management of files across numerous
|
||||
commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
|
||||
**aufs**, **DrivePool**, etc.](project_comparisons.md)
|
||||
**aufs**, **DrivePool**, etc.](project_comparisons.md) Allowing for
|
||||
the aggregation of storage without the more rigid requirements and
|
||||
failure conditions of [RAID and other
|
||||
technologies](project_comparisons.md).
|
||||
|
||||
|
||||
## Features
|
||||
@@ -23,7 +26,8 @@ commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
|
||||
* Works with filesystems of [almost any
|
||||
type](faq/compatibility_and_integration.md#what-filesystems-can-be-used-as-branches)
|
||||
* Ignore read-only filesystems when creating files
|
||||
* Hard link [copy-on-write / CoW](config/link_cow.md)
|
||||
* [Hard links](faq/technical_behavior_and_limitations.md#do-hard-links-work)
|
||||
* Hard link [copy-on-write / CoW](config/link-cow.md)
|
||||
* [Runtime configurable](runtime_interface.md)
|
||||
* Support for extended attributes (xattrs)
|
||||
* Support for file attributes (chattr)
|
||||
@@ -34,7 +38,8 @@ commodity storage devices. It is similar to [**mhddfs**, **unionfs**,
|
||||
|
||||
* Read/write overlay on top of read-only filesystem like OverlayFS
|
||||
* File whiteout
|
||||
* RAID like parity calculation (see [SnapRAID](https://www.snapraid.it))
|
||||
* RAID like redundancy (see [SnapRAID](https://www.snapraid.it) and
|
||||
[NonRAID](https://github.com/qvr/nonraid))
|
||||
* Redundancy
|
||||
* Splitting of files across branches
|
||||
|
||||
@@ -56,8 +61,9 @@ which branch will be selected for the creation.
|
||||
For functions which change attributes or remove the file the behavior
|
||||
may be applied to all instances found.
|
||||
|
||||
Read more about [policies
|
||||
here](config/functions_categories_policies.md).
|
||||
The way in which mergerfs behaves is controlled by the
|
||||
[config/options/settings](config/options.md). More specifically by
|
||||
[policies](config/functions_categories_policies.md).
|
||||
|
||||
|
||||
### Visualization
|
||||
@@ -74,7 +80,7 @@ A + B = C
|
||||
| | |
|
||||
+-- /dir2 | +-- /dir2
|
||||
| | | | |
|
||||
| *-- file4 | | +-- file4
|
||||
| +-- file4 | | +-- file4
|
||||
| | |
|
||||
| +-- /dir3 +-- /dir3
|
||||
| | | | |
|
||||
@@ -88,3 +94,16 @@ A + B = C
|
||||
## Getting Started
|
||||
|
||||
Head to the [quick start guide](quickstart.md).
|
||||
|
||||
|
||||
## About This Documentation
|
||||
|
||||
* Like the software the documentation changes over time. Ensure that
|
||||
you are reading the documentation related to the version of the
|
||||
software you are using.
|
||||
* The documentation is explicit, literal, and reasonably thorough. If
|
||||
a suspected feature is not mentioned it does not exist. Do not read
|
||||
into the wording. What is described is how it functions. If you feel
|
||||
like something is not explained sufficiently or missing please [ask
|
||||
in one of the supported forums](support.md#contact-issue-submission)
|
||||
and the docs will be updated.
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
block to change credentials as required by numerous filesystem
|
||||
functions. This impacts performance.
|
||||
* FreeBSD's FUSE implementation is lacking many features of Linux.
|
||||
* passthrough
|
||||
* IO passthrough
|
||||
* statx
|
||||
* lazy umount
|
||||
* oom_score_adj
|
||||
* fuse_msg_size
|
||||
* fuse-msg-size
|
||||
* kernel symlink caching
|
||||
* kernel readdir caching
|
||||
* writeback caching
|
||||
@@ -23,13 +23,18 @@
|
||||
|
||||
#### Supplemental group caching
|
||||
|
||||
Due to the overhead of
|
||||
[getgroups/setgroups](http://linux.die.net/man/2/setgroups) mergerfs
|
||||
utilizes a cache. As necessary the supplemental group information will
|
||||
be queried and cached. That cached list of groups will be used to set
|
||||
the supplement groups as necessary. Due to the high cost of querying
|
||||
the group list the default expiry for said data is 1 hour and after 12
|
||||
hours of no usage will be removed from the cache all together.
|
||||
Due to the high cost of querying supplemental groups it is necessary
|
||||
for mergerfs to cache the list. As a result if supplemental groups are
|
||||
changed while mergerfs is running the cache may become stale. This
|
||||
generally isn't a problem as these groups tend not to be changed
|
||||
often. However, the cache has a default expiry of 1 hour and after 12
|
||||
hours of no usage the cache entry will be removed altogether.
|
||||
|
||||
See [gidcache.expire-timeout and
|
||||
gid-cache.remove-timeout](config/options.md) and the [runtime
|
||||
interface](runtime_interface.md#commands) for forcing expiry and clearing of
|
||||
the cache.
|
||||
|
||||
|
||||
#### Host vs Container identity
|
||||
|
||||
@@ -91,36 +96,48 @@ on [page caching](config/cache.md).
|
||||
|
||||
## 3rd Party Software
|
||||
|
||||
### SQLite3, Plex, Jellyfin do not work with mergerfs
|
||||
### Software using mmap
|
||||
|
||||
It does. If you're trying to put the software's config / metadata /
|
||||
database on mergerfs you can't set [cache.files=off](config/cache.md)
|
||||
(unless you use Linux v6.6 or above and
|
||||
[direct-io-allow-mmap](config/options.md) is enabled) because they are
|
||||
using **sqlite3** with **mmap** enabled and have failed to properly
|
||||
handle the situation where **mmap** may not be available.
|
||||
* software using sqlite3 w/ mmap
|
||||
* Plex (for config)
|
||||
* Jellyfin (for config)
|
||||
* Emby (for config)
|
||||
* Radarr
|
||||
* Sonarr
|
||||
* Lidarr
|
||||
* other
|
||||
* rtorrent
|
||||
* qbittorrent (with 'Disk IO type = Memory mapped files')
|
||||
|
||||
[mmap](https://en.wikipedia.org/wiki/Mmap) is a filesystem and
|
||||
operating system feature allowing files to be interacted with as if it
|
||||
was normal memory. Support for `mmap` is **required** by a number of
|
||||
projects. FUSE (and therefore mergerfs), in order to support `mmap`,
|
||||
requires [page caching](config/cache.md) to be
|
||||
enabled. IE `cache.files` must not be set to `off` (if using Linux before
|
||||
v6.6 or mergerfs before v2.41.0.)
|
||||
|
||||
In newer versions of mergerfs and Linux `off` will work as it can now
|
||||
auto enable page caching (and therefore `mmap`) as needed.
|
||||
|
||||
That said it is recommended that config and runtime files be stored on
|
||||
SSDs on a regular filesystem for performance reasons. See [What should
|
||||
mergerfs NOT be used
|
||||
for?](faq/recommendations_and_warnings.md#what-should-mergerfs-not-be-used-for).
|
||||
|
||||
Other software that leverages **sqlite3** which require **mmap**
|
||||
includes Radarr, Sonarr, and Lidarr. However, many programs use
|
||||
**sqlite3** and do not require **mmap**.
|
||||
Technically **sqlite3** does not require `mmap`, and in fact the
|
||||
default is not to use it, but many programs enable the feature and
|
||||
provide no error handling when not supported. Ideally the software
|
||||
would catch the error and use traditional IO instead. Not only is it
|
||||
more compatible but could also be more performant in certain
|
||||
situations.
|
||||
|
||||
It is recommended that you reach out to the developers of the software
|
||||
you are having troubles with and ask them to add a fallback to regular
|
||||
file IO when **mmap** is unavailable. It is not only more compatible
|
||||
but also can be more performant in certain situations.
|
||||
|
||||
If the issue is that quick scans do not seem to pick up media then be
|
||||
sure to set `func.getattr=newest`. That said a full scan will pick up
|
||||
all media and it will put less load on the host to use time based
|
||||
library scans or to configure downloading software to trigger a scan
|
||||
when files are added to the pool. See [Does inotify and fanotify
|
||||
work?](faq/compatibility_and_integration.md#does-inotify-and-fanotify-work)
|
||||
for more details.
|
||||
### rsync
|
||||
|
||||
This isn't specific to mergerfs but when using `--sparse, -S` option
|
||||
with rsync the block size used to read and write data changes from
|
||||
256KiB to 1KiB. This significantly impacts performance.
|
||||
|
||||
|
||||
### backup software
|
||||
|
||||
@@ -64,7 +64,8 @@
|
||||
* 2024-02-22 - [Setup and Install MergerFS and SnapRAID (Part 2)](https://noted.lol/mergerfs-and-snapraid-setup-part-2/)
|
||||
* 2024-11-15 - [Meu servidor NAS - Parte 18: Recuperando um HD, recuperando o MergerFS e os próximos passos do NAS!](https://www.youtube.com/watch?v=5fy98kPzE3s)
|
||||
* 2025-04-23 - [How to build the Perfect Media Server | Part 1 - The Tech Stack | mergerfs, SnapRAID, and docker.](https://www.youtube.com/watch?v=Yt67zz9p0FU)
|
||||
* 2025-06-12 - [DIY Unraid-like functionality ](https://www.youtube.com/watch?v=fR0XhaWvR1E)
|
||||
* 2025-06-12 - [DIY Unraid-like functionality](https://www.youtube.com/watch?v=fR0XhaWvR1E)
|
||||
* 2025-09-22 - [استفاده از Snapraid و MergerFS | جایگزین RAID5](https://www.youtube.com/watch?v=GA6OvaUBKXs)
|
||||
|
||||
|
||||
## Podcasts
|
||||
|
||||
@@ -24,23 +24,22 @@ before changing them to understand how functionality will change.
|
||||
|
||||
* test theoretical performance using `nullrw` or using a ram disk as a
|
||||
branch
|
||||
* enable [passthrough](config/passthrough.md) (likely to have the
|
||||
* enable [passthrough.io](config/passthrough.md) (likely to have the
|
||||
biggest impact)
|
||||
* change read or process [thread pools](config/threads.md)
|
||||
* toggle [func.readdir](config/func_readdir.md)
|
||||
* increase [readahead](config/readahead.md): `readahead=1024`
|
||||
* disable `security_capability` and/or [xattr](config/xattr.md)
|
||||
* disable `security-capability` and/or [xattr](config/xattr.md)
|
||||
* increase cache timeouts [cache.attr](config/cache.md#cacheattr),
|
||||
[cache.entry](config/cache.md#cacheentry),
|
||||
[cache.negative_entry](config/cache.md#cachenegative_entry)
|
||||
[cache.negative-entry](config/cache.md#cachenegative-entry)
|
||||
* toggle [page caching](config/cache.md#cachefiles)
|
||||
* enable `parallel-direct-writes`
|
||||
* enable [cache.writeback](config/cache.md#cachewriteback)
|
||||
* enable [cache.statfs](config/cache.md#cachestatfs)
|
||||
* enable [cache.symlinks](config/cache.md#cachesymlinks)
|
||||
* enable [cache.readdir](config/cache.md#cachereaddir)
|
||||
* disable `posix_acl`
|
||||
* disable `async_read`
|
||||
* disable `posix-acl`
|
||||
* disable `async-read`
|
||||
* use [symlinkify](config/symlinkify.md) if your data is largely
|
||||
static and read-only
|
||||
* use [tiered cache](extended_usage_patterns.md) devices
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Project Comparisons
|
||||
|
||||
There are no solutions, only trade-offs.
|
||||
|
||||
|
||||
## mhddfs
|
||||
|
||||
* [https://romanrm.net/mhddfs](https://romanrm.net/mhddfs)
|
||||
|
||||
+30
-17
@@ -25,24 +25,22 @@ cases.
|
||||
* func.getattr=newest
|
||||
* dropcacheonclose=false
|
||||
|
||||
In previous versions of Linux it was unable to support `mmap` if [page
|
||||
caching](config/cache.md) was disabled (ie:
|
||||
`cache.files=off`). However, it now will enable page caching if needed
|
||||
for a particular file if `mmap` is requested.
|
||||
See [Known Issues and Bugs#Software Using
|
||||
mmap](known_issues_bugs.md#software-using-mmap) for more details why
|
||||
the Linux and mergerfs version matter and its relation to `mmap`.
|
||||
|
||||
[mmap](https://en.wikipedia.org/wiki/Mmap) is needed by certain
|
||||
software to read and write to a file. However, many software could
|
||||
work without it and fail to have proper error handling for when it is
|
||||
unavailable. Many programs that use **sqlite3** will require `mmap`
|
||||
despite [sqlite3 working perfectly
|
||||
fine](known_issues_bugs.md#sqlite3-plex-jellyfin-do-not-work-with-mergerfs)
|
||||
without it (and in some cases can be more performant with regular file
|
||||
IO.)
|
||||
**NOTE:** This "auto mmap" feature is only supported in mergerfs
|
||||
v2.41.0 and above. For older versions see below or the docs from that
|
||||
release.
|
||||
|
||||
|
||||
### You use Linux v6.5 or below
|
||||
|
||||
#### You need `mmap` (used by rtorrent and many sqlite3 base software)
|
||||
#### You need `mmap`
|
||||
|
||||
To keep the details in a single location: see [Known Issues and
|
||||
Bugs#Software Using mmap](known_issues_bugs.md#software-using-mmap)
|
||||
for more details.
|
||||
|
||||
* cache.files=auto-full
|
||||
* category.create=pfrd
|
||||
@@ -66,6 +64,15 @@ branches](config/branches.md#branch-setup).
|
||||
|
||||
### Command Line
|
||||
|
||||
```
|
||||
# Two positional options: `:` colon separated branches and mountpoint
|
||||
mergerfs -o opt[,opt...] /branch0:/branch1 /mountpoint
|
||||
# or list of positional options where the last is the mountpoint
|
||||
mergerfs -o opt[,opt...] /branch0 /branch1 /mountpoint
|
||||
# or set mountpoint via -o mountpoint
|
||||
mergerfs -o mountpoint=/mountpoint /branch0 /branch1
|
||||
```
|
||||
|
||||
```
|
||||
mergerfs -o cache.files=off,category.create=pfrd,func.getattr=newest,dropcacheonclose=false /mnt/hdd0:/mnt/hdd1 /media
|
||||
```
|
||||
@@ -131,9 +138,12 @@ ExecStart=/usr/bin/mergerfs \
|
||||
-o category.create=pfrd \
|
||||
-o func.getattr=newest \
|
||||
-o dropcacheonclose=false \
|
||||
/mnt/hdd0:/mnt/hdd1 \
|
||||
/mnt/hdd0 \
|
||||
/mnt/hdd1 \
|
||||
/media
|
||||
ExecStop=/bin/fusermount -uz /media
|
||||
ExecStop=/usr/bin/umount /media
|
||||
# Or if you need fusermount
|
||||
# ExecStop=/usr/bin/mergerfs-fusermount -u /media
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
@@ -195,9 +205,12 @@ ExecStart=/usr/bin/mergerfs \
|
||||
-o category.create=pfrd \
|
||||
-o func.getattr=newest \
|
||||
-o dropcacheonclose=false \
|
||||
/mnt/hdd0:/mnt/hdd1 \
|
||||
/mnt/hdd0 \
|
||||
/mnt/hdd1 \
|
||||
/media
|
||||
ExecStop=/bin/fusermount -uz /media
|
||||
ExecStop=/usr/bin/umount /media
|
||||
# Or if you need fusermount
|
||||
# ExecStop=/usr/bin/mergerfs-fusermount -u /media
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
* [snapraid](https://www.snapraid.it/): a backup program designed for
|
||||
disk arrays, storing parity information for data recovery in the
|
||||
event of up to six disk failures.
|
||||
* [nonraid](https://github.com/qvr/nonraid): NonRAID is a fork of the
|
||||
unRAID system's open-source md_unraid kernel driver enabling
|
||||
UnRAID-style storage arrays with parity protection outside of the
|
||||
commercial UnRAID system.
|
||||
* [rclone](https://rclone.org/): a command-line program to manage
|
||||
files on cloud storage. It is a feature-rich alternative to cloud
|
||||
vendors' web storage interfaces. rclone's
|
||||
|
||||
@@ -49,6 +49,23 @@ NFS export settings:
|
||||
running mergerfs as a non-root user, there is no guarantee that
|
||||
mergerfs will be able to do what it needs to manage the underlying
|
||||
filesystem.
|
||||
* `softerr` or `soft`: A perhaps questionable choice but if NFS
|
||||
freezes up because the backend dies or the network goes down the
|
||||
default `hard` mount would cause mergerfs to block the same as any
|
||||
other software. By setting `softerr` or `soft` the NFS client will
|
||||
timeout eventually and return an error.
|
||||
* `softreval`: NFS client will serve up cached data after `retrans`
|
||||
attempts to revalidate the data. Helps with intermitent network
|
||||
issues.
|
||||
* `timeo=150`: Timeout till retrying request.
|
||||
* `retrans=3`: Number of retrying a request.
|
||||
* `rsize=131072`: Read size.
|
||||
* `wsize=131072`: Write size.
|
||||
|
||||
`no_root_squash` is necessary but the others are just suggestions and
|
||||
you may want to alter based on your use case. The risk of using `hard`
|
||||
and other more strict behavior options is that should NFS completely
|
||||
lock up it will impact mergerfs negatively.
|
||||
|
||||
|
||||
### NFS exporting mergerfs
|
||||
@@ -59,19 +76,21 @@ but if you run into problems it may be worth trying Samba/SMB.
|
||||
|
||||
**mergerfs settings:**
|
||||
|
||||
* `noforget`
|
||||
* `noforget` or `remember-nodes=SECONDS`
|
||||
* `inodecalc=path-hash`
|
||||
* `lazy-umount-mountpoint=false`
|
||||
|
||||
`noforget` is needed because NFS uses the `name_to_handle_at` and
|
||||
`noforget` (or `remember-nodes` with a large value such as `86400` for
|
||||
a full day) is needed because NFS uses the `name_to_handle_at` and
|
||||
`open_by_handle_at` functions which allow a program to keep a
|
||||
reference to a file without technically having it open in the typical
|
||||
sense. The problem is that FUSE has no way to know that NFS has a
|
||||
handle that it will later use to open the file again. As a result, it
|
||||
is possible for the kernel to tell mergerfs to forget about the file
|
||||
node and should NFS ever ask for that node's details in the future it
|
||||
would have nothing to respond with. Keeping nodes around forever is
|
||||
not ideal but at the moment the only way to manage the situation.
|
||||
would have nothing to respond with. Keeping nodes around forever (or
|
||||
however long is set with `remember-nodes`) is not ideal but at the
|
||||
moment the only way to manage the situation.
|
||||
|
||||
`inodecalc=path-hash` is needed because NFS is sensitive to
|
||||
out-of-band changes. FUSE doesn't care if a file's inode value changes
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* readdir concurrency
|
||||
* memory
|
||||
* 1MB+ pre reader thread + inflight processing for messages
|
||||
depending on [fuse_msg_size](config/fuse_msg_size.md)
|
||||
depending on [fuse-msg-size](config/fuse-msg-size.md)
|
||||
* buffers allocated temporarily for reading directories
|
||||
* [gidcache](faq/technical_behavior_and_limitations.md#how-does-mergerfs-handle-credentials)
|
||||
* FUSE nodes
|
||||
|
||||
@@ -44,6 +44,16 @@ sudo dpkg -i mergerfs_<ver>.debian-<rel>_<arch>.deb
|
||||
|
||||
#### Ubuntu
|
||||
|
||||
Given non-LTS releases are rarely used for servers and only have [9
|
||||
months of support](https://ubuntu.com/about/release-cycle) we do not
|
||||
build packages for them by default. It is recommended that you first
|
||||
try the `deb` file for the LTS release most closely related to your
|
||||
non-LTS release. If that does not work you can use the [static
|
||||
build](#static-linux-binaries) or [file a
|
||||
ticket](https://github.com/trapexit/mergerfs/issues) requesting a
|
||||
package built.
|
||||
|
||||
|
||||
```
|
||||
wget https://github.com/trapexit/mergerfs/releases/download/<version>/mergerfs_<ver>.ubuntu-<rel>_<arch>.deb
|
||||
sudo dpkg -i mergerfs_<ver>.ubuntu-<rel>_<arch>.deb
|
||||
|
||||
@@ -47,6 +47,7 @@ directly.](mailto:support@spawn.link)**
|
||||
programs: `ln`, `mv`, `cp`, `ls`, `dd`, etc. With the most simple
|
||||
mergerfs setup possible.
|
||||
|
||||
|
||||
### Contact / Issue submission
|
||||
|
||||
* github.com: [https://github.com/trapexit/mergerfs/issues](https://github.com/trapexit/mergerfs/issues)
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
compliant filesystems. mergerfs doesn't generally make exceptions
|
||||
for FAT, NTFS, or other non-POSIX filesystem.
|
||||
* Unless using Linux v6.6 or above do **not** use `cache.files=off` if
|
||||
you expect applications (such as rtorrent) to use
|
||||
you expect applications to use
|
||||
[mmap](http://linux.die.net/man/2/mmap). Enabling `dropcacheonclose`
|
||||
is recommended when `cache.files=auto-full`.
|
||||
is recommended when `cache.files=auto-full`. See
|
||||
[quickstart](quickstart.md).
|
||||
* [Kodi](http://kodi.tv), [Plex](http://plex.tv),
|
||||
[Subsonic](http://subsonic.org), etc. can use directory
|
||||
[mtime](http://linux.die.net/man/2/stat) to more efficiently
|
||||
|
||||
@@ -50,8 +50,8 @@ OPTIONS:
|
||||
|
||||
EXPERIMENTAL
|
||||
|
||||
For some time there has been work to enable
|
||||
[passthrough](config/passthrough.md) IO in FUSE. Passthrough IO would
|
||||
For some time there has been work to enable IO
|
||||
[passthrough](config/passthrough.md) in FUSE. IO Passthrough would
|
||||
allow for near native performance with regards to reads and writes (at
|
||||
the expense of certain mergerfs features.) In Linux v6.9 that feature
|
||||
made its way into the kernel however in a somewhat limited form which
|
||||
|
||||
+5
-4
@@ -81,8 +81,9 @@ nav:
|
||||
- config/inodecalc.md
|
||||
- config/threads.md
|
||||
- config/pin-threads.md
|
||||
- config/link_cow.md
|
||||
- config/fuse_msg_size.md
|
||||
- config/proxy-ioprio.md
|
||||
- config/link-cow.md
|
||||
- config/fuse-msg-size.md
|
||||
- config/follow-symlinks.md
|
||||
- config/link-exdev.md
|
||||
- config/rename-exdev.md
|
||||
@@ -107,9 +108,9 @@ nav:
|
||||
- extended_usage_patterns.md
|
||||
- FAQ:
|
||||
- faq/why_isnt_it_working.md
|
||||
- faq/reliability_and_scalability.md
|
||||
- faq/usage_and_functionality.md
|
||||
- faq/configuration_and_policies.md
|
||||
- faq/usage_and_functionality.md
|
||||
- faq/reliability_and_scalability.md
|
||||
- faq/compatibility_and_integration.md
|
||||
- faq/recommendations_and_warnings.md
|
||||
- faq/technical_behavior_and_limitations.md
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
set -x
|
||||
|
||||
current_branch="$(git branch --show-current)"
|
||||
|
||||
mike delete --all --push --allow-empty --ignore-remote-status
|
||||
|
||||
git checkout "master"
|
||||
mike deploy --push "preview" "master"
|
||||
|
||||
for tag in $(git tag -l | sort --version-sort --reverse)
|
||||
do
|
||||
if git ls-tree -r --name-only "${tag}" | grep -q 'mkdocs.yml'; then
|
||||
git checkout "${tag}"
|
||||
mike deploy --push "${tag}"
|
||||
fi
|
||||
done
|
||||
|
||||
latest="$(git tag -l | sort --version-sort --reverse | head -n 1)"
|
||||
git checkout "${latest}"
|
||||
mike alias --push "${latest}" "latest"
|
||||
|
||||
mike set-default --push "preview"
|
||||
|
||||
git checkout "${current_branch}"
|
||||
@@ -64,6 +64,7 @@ Branch::to_string(void) const
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(std::holds_alternative<u64>(_minfreespace))
|
||||
{
|
||||
rv += ',';
|
||||
|
||||
+2
-1
@@ -21,6 +21,7 @@
|
||||
#include "int_types.h"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
public:
|
||||
std::variant<u64,const u64*> _minfreespace;
|
||||
Mode mode;
|
||||
std::string path;
|
||||
fs::path path;
|
||||
|
||||
public:
|
||||
Branch();
|
||||
|
||||
+3
-3
@@ -391,10 +391,10 @@ Branches::Impl::to_paths(StrVec &paths_) const
|
||||
}
|
||||
}
|
||||
|
||||
fs::PathVector
|
||||
std::vector<fs::path>
|
||||
Branches::Impl::to_paths() const
|
||||
{
|
||||
fs::PathVector vp;
|
||||
std::vector<fs::path> vp;
|
||||
|
||||
for(const auto &branch : *this)
|
||||
vp.emplace_back(branch.path);
|
||||
@@ -449,7 +449,7 @@ Branches::find_and_set_mode_ro()
|
||||
continue;
|
||||
|
||||
SysLog::warning("branch `{}` found to be readonly - setting its mode=RO",
|
||||
branch.path);
|
||||
branch.path.string());
|
||||
|
||||
branch.mode = Branch::Mode::RO;
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "fs_pathvector.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
public:
|
||||
const u64 &minfreespace(void) const;
|
||||
void to_paths(StrVec &strvec) const;
|
||||
fs::PathVector to_paths() const;
|
||||
std::vector<fs::path> to_paths() const;
|
||||
|
||||
public:
|
||||
Impl& operator=(Impl &impl_);
|
||||
|
||||
+305
-266
@@ -18,12 +18,13 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "nonstd/string.hpp"
|
||||
#include "num.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "str.hpp"
|
||||
#include "to_string.hpp"
|
||||
#include "version.hpp"
|
||||
#include "nonstd/string.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@@ -38,208 +39,264 @@
|
||||
|
||||
#define MINFREESPACE_DEFAULT (4294967295ULL)
|
||||
|
||||
#define IFERT(S) if(S == s_) return true
|
||||
|
||||
constexpr static const char CACHE_FILES_PROCESS_NAMES_DEFAULT[] =
|
||||
"rtorrent|"
|
||||
"qbittorrent-nox";
|
||||
|
||||
Config cfg;
|
||||
|
||||
namespace l
|
||||
Config::CfgConfigFile::CfgConfigFile()
|
||||
{
|
||||
static
|
||||
bool
|
||||
readonly(const std::string &s_)
|
||||
{
|
||||
IFERT("async_read");
|
||||
IFERT("branches-mount-timeout");
|
||||
IFERT("branches-mount-timeout-fail");
|
||||
IFERT("cache.symlinks");
|
||||
IFERT("cache.writeback");
|
||||
IFERT("direct-io-allow-mmap");
|
||||
IFERT("export-support");
|
||||
IFERT("fsname");
|
||||
IFERT("fuse_msg_size");
|
||||
IFERT("kernel-permissions-check");
|
||||
IFERT("mount");
|
||||
IFERT("nullrw");
|
||||
IFERT("pid");
|
||||
IFERT("pin-threads");
|
||||
IFERT("process-thread-count");
|
||||
IFERT("process-thread-queue-depth");
|
||||
IFERT("read-thread-count");
|
||||
IFERT("readdirplus");
|
||||
IFERT("scheduling-priority");
|
||||
IFERT("srcmounts");
|
||||
IFERT("threads");
|
||||
IFERT("version");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
: async_read(true),
|
||||
auto_cache(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
branches(minfreespace),
|
||||
branches_mount_timeout(0),
|
||||
branches_mount_timeout_fail(false),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::OFF),
|
||||
cache_files_process_names(CACHE_FILES_PROCESS_NAMES_DEFAULT),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
category(func),
|
||||
direct_io(false),
|
||||
direct_io_allow_mmap(true),
|
||||
dropcacheonclose(false),
|
||||
export_support(true),
|
||||
flushonclose(FlushOnClose::ENUM::OPENED_FOR_WRITE),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size("1M"),
|
||||
gid_cache_expire_timeout(60 * 60),
|
||||
gid_cache_remove_timeout(60 * 60 * 12),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
lazy_umount_mountpoint(false),
|
||||
link_cow(false),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
log_metrics(false),
|
||||
mountpoint(),
|
||||
moveonenospc(true),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
nullrw(false),
|
||||
parallel_direct_writes(true),
|
||||
posix_acl(false),
|
||||
readahead(0),
|
||||
readdir("seq"),
|
||||
readdirplus(false),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
scheduling_priority(-10),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
fuse_read_thread_count(0),
|
||||
fuse_process_thread_count(-1),
|
||||
fuse_process_thread_queue_depth(2),
|
||||
fuse_pin_threads("false"),
|
||||
version(MERGERFS_VERSION),
|
||||
writeback_cache(false),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH),
|
||||
_initialized(false)
|
||||
{
|
||||
_map["async_read"] = &async_read;
|
||||
_map["auto_cache"] = &auto_cache;
|
||||
_map["branches"] = &branches;
|
||||
_map["branches-mount-timeout"] = &branches_mount_timeout;
|
||||
_map["branches-mount-timeout-fail"] = &branches_mount_timeout_fail;
|
||||
_map["cache.attr"] = &cache_attr;
|
||||
_map["cache.entry"] = &cache_entry;
|
||||
_map["cache.files"] = &cache_files;
|
||||
_map["cache.files.process-names"] = &cache_files_process_names;
|
||||
_map["cache.negative_entry"] = &cache_negative_entry;
|
||||
_map["cache.readdir"] = &cache_readdir;
|
||||
_map["cache.statfs"] = &cache_statfs;
|
||||
_map["cache.symlinks"] = &cache_symlinks;
|
||||
_map["cache.writeback"] = &writeback_cache;
|
||||
_map["category.action"] = &category.action;
|
||||
_map["category.create"] = &category.create;
|
||||
_map["category.search"] = &category.search;
|
||||
_map["direct_io"] = &direct_io;
|
||||
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["export-support"] = &export_support;
|
||||
_map["flush-on-close"] = &flushonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_map["fsname"] = &fsname;
|
||||
_map["func.access"] = &func.access;
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
_map["func.chown"] = &func.chown;
|
||||
_map["func.create"] = &func.create;
|
||||
_map["func.getattr"] = &func.getattr;
|
||||
_map["func.getxattr"] = &func.getxattr;
|
||||
_map["func.link"] = &func.link;
|
||||
_map["func.listxattr"] = &func.listxattr;
|
||||
_map["func.mkdir"] = &func.mkdir;
|
||||
_map["func.mknod"] = &func.mknod;
|
||||
_map["func.open"] = &func.open;
|
||||
_map["func.readdir"] = &readdir;
|
||||
_map["func.readlink"] = &func.readlink;
|
||||
_map["func.removexattr"] = &func.removexattr;
|
||||
_map["func.rename"] = &func.rename;
|
||||
_map["func.rmdir"] = &func.rmdir;
|
||||
_map["func.setxattr"] = &func.setxattr;
|
||||
_map["func.symlink"] = &func.symlink;
|
||||
_map["func.truncate"] = &func.truncate;
|
||||
_map["func.unlink"] = &func.unlink;
|
||||
_map["func.utimens"] = &func.utimens;
|
||||
_map["fuse_msg_size"] = &fuse_msg_size;
|
||||
_map["gid-cache-expire-timeout"] = &gid_cache_expire_timeout;
|
||||
_map["gid-cache-remove-timeout"] = &gid_cache_remove_timeout;
|
||||
_map["handle-killpriv"] = &handle_killpriv;
|
||||
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
|
||||
_map["ignorepponrename"] = &ignorepponrename;
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel_cache"] = &kernel_cache;
|
||||
_map["kernel-permissions-check"] = &kernel_permissions_check;
|
||||
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
|
||||
_map["link_cow"] = &link_cow;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["log.metrics"] = &log_metrics;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &mountpoint;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
_map["nfsopenhack"] = &nfsopenhack;
|
||||
_map["nullrw"] = &nullrw;
|
||||
_map["passthrough"] = &passthrough;
|
||||
_map["pid"] = &pid;
|
||||
_map["parallel-direct-writes"] = ¶llel_direct_writes;
|
||||
_map["pin-threads"] = &fuse_pin_threads;
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
_map["readahead"] = &readahead;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["scheduling-priority"] = &scheduling_priority;
|
||||
_map["security_capability"] = &security_capability;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
_map["statfs_ignore"] = &statfs_ignore;
|
||||
_map["symlinkify"] = &symlinkify;
|
||||
_map["symlinkify_timeout"] = &symlinkify_timeout;
|
||||
_map["threads"] = &fuse_read_thread_count;
|
||||
_map["read-thread-count"] = &fuse_read_thread_count;
|
||||
_map["process-thread-count"] = &fuse_process_thread_count;
|
||||
_map["process-thread-queue-depth"] = &fuse_process_thread_queue_depth;
|
||||
_map["version"] = &version;
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
Config&
|
||||
Config::operator=(const Config &cfg_)
|
||||
int
|
||||
Config::CfgConfigFile::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
std::string val;
|
||||
fs::path cfg_file;
|
||||
|
||||
for(auto &kv : _map)
|
||||
{
|
||||
rv = cfg_.get(kv.first,&val);
|
||||
if(rv)
|
||||
continue;
|
||||
if(_depth > 5)
|
||||
return -ELOOP;
|
||||
_depth++;
|
||||
|
||||
kv.second->from_string(val);
|
||||
}
|
||||
cfg_file = (s_.empty() ? _cfg_file : s_);
|
||||
|
||||
return *this;
|
||||
rv = cfg.from_file(cfg_file);
|
||||
if(rv == 0)
|
||||
_cfg_file = cfg_file;
|
||||
|
||||
_depth--;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::string
|
||||
Config::CfgConfigFile::to_string() const
|
||||
{
|
||||
return _cfg_file;
|
||||
}
|
||||
|
||||
|
||||
Config::Config()
|
||||
:
|
||||
allow_idmap(false),
|
||||
async_read(true),
|
||||
branches(minfreespace),
|
||||
branches_mount_timeout(0),
|
||||
branches_mount_timeout_fail(false),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::OFF),
|
||||
cache_files_process_names(CACHE_FILES_PROCESS_NAMES_DEFAULT),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
cache_writeback(false),
|
||||
category(func),
|
||||
config_file(),
|
||||
congestion_threshold(fuse_cfg.congestion_threshold,0),
|
||||
debug(fuse_cfg.debug,false),
|
||||
direct_io_allow_mmap(true),
|
||||
dropcacheonclose(false),
|
||||
export_support(true),
|
||||
flushonclose(FlushOnClose::ENUM::OPENED_FOR_WRITE),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size("1M"),
|
||||
gid(fuse_cfg.gid,FUSE_CFG_INVALID_ID),
|
||||
gid_cache_expire_timeout(60 * 60),
|
||||
gid_cache_remove_timeout(60 * 60 * 12),
|
||||
handle_killpriv(true),
|
||||
handle_killpriv_v2(true),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
kernel_permissions_check(true),
|
||||
lazy_umount_mountpoint(false),
|
||||
link_cow(false),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
log_metrics(false),
|
||||
max_background(fuse_cfg.max_background,0),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
mountpoint(),
|
||||
_mount(mountpoint),
|
||||
_mountpoint(mountpoint),
|
||||
moveonenospc(true),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
noforget(),
|
||||
nullrw(false),
|
||||
parallel_direct_writes(true),
|
||||
passthrough_io(PassthroughIO::ENUM::OFF),
|
||||
passthrough_max_stack_depth(fuse_cfg.passthrough_max_stack_depth,1),
|
||||
pin_threads(fuse_cfg.pin_threads,"false"),
|
||||
posix_acl(false),
|
||||
process_thread_count(fuse_cfg.process_thread_count,-1),
|
||||
process_thread_queue_depth(fuse_cfg.process_thread_queue_depth,2),
|
||||
proxy_ioprio(false),
|
||||
read_thread_count(fuse_cfg.read_thread_count,0),
|
||||
readahead(0),
|
||||
readdir("seq"),
|
||||
remember_nodes(fuse_cfg.remember_nodes,0),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
scheduling_priority(-10),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
threads(fuse_cfg.read_thread_count),
|
||||
uid(fuse_cfg.uid,FUSE_CFG_INVALID_ID),
|
||||
umask(fuse_cfg.umask,FUSE_CFG_INVALID_UMASK),
|
||||
version(MERGERFS_VERSION),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH),
|
||||
_initialized(false)
|
||||
{
|
||||
allow_idmap.ro =
|
||||
async_read.ro =
|
||||
branches_mount_timeout.ro =
|
||||
branches_mount_timeout_fail.ro =
|
||||
cache_symlinks.ro =
|
||||
cache_writeback.ro =
|
||||
congestion_threshold.ro =
|
||||
direct_io_allow_mmap.ro =
|
||||
export_support.ro =
|
||||
fsname.ro =
|
||||
fuse_msg_size.ro =
|
||||
handle_killpriv.ro =
|
||||
handle_killpriv_v2.ro =
|
||||
kernel_permissions_check.ro =
|
||||
max_background.ro =
|
||||
_mount.ro =
|
||||
_mountpoint.ro =
|
||||
nullrw.ro =
|
||||
pid.ro =
|
||||
pin_threads.ro =
|
||||
process_thread_count.ro =
|
||||
process_thread_queue_depth.ro =
|
||||
read_thread_count.ro =
|
||||
scheduling_priority.ro =
|
||||
srcmounts.ro =
|
||||
version.ro =
|
||||
true;
|
||||
congestion_threshold.ro =
|
||||
gid.display =
|
||||
max_background.ro =
|
||||
threads.display =
|
||||
_mount.display =
|
||||
uid.display =
|
||||
umask.display =
|
||||
false;
|
||||
|
||||
_map["allow-idmap"] = &allow_idmap;
|
||||
_map["async-read"] = &async_read;
|
||||
_map["atomic-o-trunc"] = &_dummy;
|
||||
_map["auto-cache"] = &_dummy;
|
||||
_map["big-writes"] = &_dummy;
|
||||
_map["branches"] = &branches;
|
||||
_map["branches-mount-timeout"] = &branches_mount_timeout;
|
||||
_map["branches-mount-timeout-fail"] = &branches_mount_timeout_fail;
|
||||
_map["cache.attr"] = &cache_attr;
|
||||
_map["cache.entry"] = &cache_entry;
|
||||
_map["cache.files"] = &cache_files;
|
||||
_map["cache.files.process-names"] = &cache_files_process_names;
|
||||
_map["cache.negative-entry"] = &cache_negative_entry;
|
||||
_map["cache.open"] = &_dummy;
|
||||
_map["cache.readdir"] = &cache_readdir;
|
||||
_map["cache.statfs"] = &cache_statfs;
|
||||
_map["cache.symlinks"] = &cache_symlinks;
|
||||
_map["cache.writeback"] = &cache_writeback;
|
||||
_map["category.action"] = &category.action;
|
||||
_map["category.create"] = &category.create;
|
||||
_map["category.search"] = &category.search;
|
||||
_map["config"] = &config_file;
|
||||
_map["defaults"] = &_dummy;
|
||||
_map["debug"] = &debug;
|
||||
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
|
||||
_map["direct-io"] = &_dummy;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["export-support"] = &export_support;
|
||||
_map["flush-on-close"] = &flushonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_map["fsname"] = &fsname;
|
||||
_map["func.access"] = &func.access;
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
_map["func.chown"] = &func.chown;
|
||||
_map["func.create"] = &func.create;
|
||||
_map["func.getattr"] = &func.getattr;
|
||||
_map["func.getxattr"] = &func.getxattr;
|
||||
_map["func.link"] = &func.link;
|
||||
_map["func.listxattr"] = &func.listxattr;
|
||||
_map["func.mkdir"] = &func.mkdir;
|
||||
_map["func.mknod"] = &func.mknod;
|
||||
_map["func.open"] = &func.open;
|
||||
_map["func.readdir"] = &readdir;
|
||||
_map["func.readlink"] = &func.readlink;
|
||||
_map["func.removexattr"] = &func.removexattr;
|
||||
_map["func.rename"] = &func.rename;
|
||||
_map["func.rmdir"] = &func.rmdir;
|
||||
_map["func.setxattr"] = &func.setxattr;
|
||||
_map["func.symlink"] = &func.symlink;
|
||||
_map["func.truncate"] = &func.truncate;
|
||||
_map["func.unlink"] = &func.unlink;
|
||||
_map["func.utimens"] = &func.utimens;
|
||||
_map["fuse-msg-size"] = &fuse_msg_size;
|
||||
_map["gid"] = &gid;
|
||||
_map["gid-cache.expire-timeout"] = &gid_cache_expire_timeout;
|
||||
_map["gid-cache.remove-timeout"] = &gid_cache_remove_timeout;
|
||||
_map["handle-killpriv"] = &handle_killpriv;
|
||||
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
|
||||
_map["hard-remove"] = &_dummy;
|
||||
_map["ignorepponrename"] = &ignorepponrename;
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel-permissions-check"] = &kernel_permissions_check;
|
||||
_map["kernel-cache"] = &_dummy;
|
||||
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["link-cow"] = &link_cow;
|
||||
_map["log.metrics"] = &log_metrics;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &_mount;
|
||||
_map["mountpoint"] = &_mountpoint;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
_map["negative-entry"] = &_dummy;
|
||||
_map["nfsopenhack"] = &nfsopenhack;
|
||||
_map["no-splice-move"] = &_dummy;
|
||||
_map["no-splice-read"] = &_dummy;
|
||||
_map["no-splice-write"] = &_dummy;
|
||||
_map["noforget"] = &noforget;
|
||||
_map["nonempty"] = &_dummy;
|
||||
_map["nullrw"] = &nullrw;
|
||||
_map["parallel-direct-writes"] = ¶llel_direct_writes;
|
||||
_map["passthrough.io"] = &passthrough_io;
|
||||
_map["passthrough.max-stack-depth"] = &passthrough_max_stack_depth;
|
||||
_map["pid"] = &pid;
|
||||
_map["pin-threads"] = &pin_threads;
|
||||
_map["posix-acl"] = &posix_acl;
|
||||
_map["process-thread-count"] = &process_thread_count;
|
||||
_map["process-thread-queue-depth"] = &process_thread_queue_depth;
|
||||
_map["proxy-ioprio"] = &proxy_ioprio;
|
||||
_map["read-thread-count"] = &read_thread_count;
|
||||
_map["readahead"] = &readahead;
|
||||
_map["remember-nodes"] = &remember_nodes;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["scheduling-priority"] = &scheduling_priority;
|
||||
_map["security-capability"] = &security_capability;
|
||||
_map["splice-move"] = &_dummy;
|
||||
_map["splice-read"] = &_dummy;
|
||||
_map["splice-write"] = &_dummy;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
_map["statfs-ignore"] = &statfs_ignore;
|
||||
_map["symlinkify"] = &symlinkify;
|
||||
_map["symlinkify-timeout"] = &symlinkify_timeout;
|
||||
_map["threads"] = &threads;
|
||||
_map["uid"] = &uid;
|
||||
_map["umask"] = &umask;
|
||||
_map["use-ino"] = &_dummy;
|
||||
_map["version"] = &version;
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -269,6 +326,9 @@ Config::keys_xattr(std::string &s_) const
|
||||
s_.reserve(1024);
|
||||
for(const auto &[key,val] : _map)
|
||||
{
|
||||
if(val->display == false)
|
||||
continue;
|
||||
|
||||
s_ += "user.mergerfs.";
|
||||
s_ += key;
|
||||
s_ += '\0';
|
||||
@@ -283,6 +343,9 @@ Config::keys_listxattr_size() const
|
||||
rv = 0;
|
||||
for(const auto &[key,val] : _map)
|
||||
{
|
||||
if(val->display == false)
|
||||
continue;
|
||||
|
||||
rv += sizeof("user.mergerfs.");
|
||||
rv += key.size();
|
||||
}
|
||||
@@ -302,6 +365,9 @@ Config::keys_listxattr(char *list_,
|
||||
|
||||
for(const auto &[key,val] : _map)
|
||||
{
|
||||
if(val->display == false)
|
||||
continue;
|
||||
|
||||
auto rv = fmt::format_to_n(list,size,
|
||||
"user.mergerfs.{}\0",
|
||||
key);
|
||||
@@ -318,9 +384,12 @@ int
|
||||
Config::get(const std::string &key_,
|
||||
std::string *val_) const
|
||||
{
|
||||
std::string key;
|
||||
Str2TFStrMap::const_iterator i;
|
||||
|
||||
i = _map.find(key_);
|
||||
key = str::replace(key_,'_','-');
|
||||
|
||||
i = _map.find(key);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
|
||||
@@ -329,27 +398,22 @@ Config::get(const std::string &key_,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Config::set_raw(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
Str2TFStrMap::iterator i;
|
||||
|
||||
i = _map.find(key_);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
|
||||
return i->second->from_string(value_);
|
||||
}
|
||||
|
||||
int
|
||||
Config::set(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
if(_initialized && l::readonly(key_))
|
||||
std::string key;
|
||||
Str2TFStrMap::iterator i;
|
||||
|
||||
key = str::replace(key_,'_','-');
|
||||
|
||||
i = _map.find(key);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
if(_initialized && i->second->ro)
|
||||
return -EROFS;
|
||||
|
||||
return set_raw(key_,value_);
|
||||
return i->second->from_string(value_);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -366,16 +430,13 @@ Config::set(const std::string &kv_)
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_stream(std::istream &istrm_,
|
||||
ErrVec *errs_)
|
||||
Config::from_stream(std::istream &istrm_)
|
||||
{
|
||||
int rv;
|
||||
std::string line;
|
||||
std::string key;
|
||||
std::string val;
|
||||
Config newcfg;
|
||||
|
||||
newcfg = *this;
|
||||
Config::ErrVec new_errs;
|
||||
|
||||
while(std::getline(istrm_,line,'\n'))
|
||||
{
|
||||
@@ -387,22 +448,21 @@ Config::from_stream(std::istream &istrm_,
|
||||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
rv = newcfg.set(key,val);
|
||||
rv = set(key,val);
|
||||
if(rv < 0)
|
||||
errs_->push_back({rv,key});
|
||||
new_errs.push_back({-rv,key+'='+val});
|
||||
}
|
||||
|
||||
if(!errs_->empty())
|
||||
return -EINVAL;
|
||||
rv = (new_errs.empty() ? 0 : -EINVAL);
|
||||
errs.insert(errs.end(),
|
||||
new_errs.begin(),
|
||||
new_errs.end());
|
||||
|
||||
*this = newcfg;
|
||||
|
||||
return 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_file(const std::string &filepath_,
|
||||
ErrVec *errs_)
|
||||
Config::from_file(const std::string &filepath_)
|
||||
{
|
||||
int rv;
|
||||
std::ifstream ifstrm;
|
||||
@@ -410,11 +470,11 @@ Config::from_file(const std::string &filepath_,
|
||||
ifstrm.open(filepath_);
|
||||
if(!ifstrm.good())
|
||||
{
|
||||
errs_->push_back({-errno,filepath_});
|
||||
errs.push_back({errno,filepath_});
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rv = from_stream(ifstrm,errs_);
|
||||
rv = from_stream(ifstrm);
|
||||
|
||||
ifstrm.close();
|
||||
|
||||
@@ -428,15 +488,15 @@ Config::finish_initializing()
|
||||
}
|
||||
|
||||
bool
|
||||
Config::is_rootdir(const char *fusepath_)
|
||||
Config::is_rootdir(const fs::path &fusepath_)
|
||||
{
|
||||
return str::eq(fusepath_,"/");
|
||||
return fusepath_.empty();
|
||||
}
|
||||
|
||||
bool
|
||||
Config::is_ctrl_file(const char *fusepath_)
|
||||
Config::is_ctrl_file(const fs::path &fusepath_)
|
||||
{
|
||||
return str::eq(fusepath_,"/.mergerfs");
|
||||
return (fusepath_ == ".mergerfs");
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -471,52 +531,31 @@ Config::prune_cmd_xattr(const std::string_view &s_)
|
||||
return {};
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config &c_)
|
||||
{
|
||||
for(const auto &[key,val] : c_._map)
|
||||
os_ << key << '=' << val->to_string() << std::endl;
|
||||
|
||||
return os_;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
std::string
|
||||
err2str(const int err_)
|
||||
Config::Err::to_string() const
|
||||
{
|
||||
switch(err_)
|
||||
std::string s;
|
||||
|
||||
switch(err)
|
||||
{
|
||||
case 0:
|
||||
return std::string();
|
||||
case -EINVAL:
|
||||
return "invalid value";
|
||||
case -ENOATTR:
|
||||
return "unknown option";
|
||||
case -EROFS:
|
||||
return "read-only option";
|
||||
break;
|
||||
case EINVAL:
|
||||
s = "invalid value";
|
||||
break;
|
||||
case ENOATTR:
|
||||
s = "unknown option";
|
||||
break;
|
||||
case EROFS:
|
||||
s = "read-only option";
|
||||
break;
|
||||
case ELOOP:
|
||||
s = "too many levels of config";
|
||||
break;
|
||||
default:
|
||||
return strerror(-err_);
|
||||
s = strerror(err);
|
||||
break;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config::ErrVec &ev_)
|
||||
{
|
||||
std::string errstr;
|
||||
|
||||
for(auto &err : ev_)
|
||||
{
|
||||
os_ << "* ERROR: ";
|
||||
errstr = err2str(err.err);
|
||||
if(!errstr.empty())
|
||||
os_ << errstr << " - ";
|
||||
os_ << err.str << std::endl;
|
||||
}
|
||||
|
||||
return os_;
|
||||
return fmt::format("{} - {}",s,str);
|
||||
}
|
||||
|
||||
+70
-34
@@ -19,6 +19,7 @@
|
||||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
#include "config_dummy.hpp"
|
||||
#include "config_flushonclose.hpp"
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "config_gidcache.hpp"
|
||||
@@ -27,9 +28,11 @@
|
||||
#include "config_log_metrics.hpp"
|
||||
#include "config_moveonenospc.hpp"
|
||||
#include "config_nfsopenhack.hpp"
|
||||
#include "config_noforget.hpp"
|
||||
#include "config_pagesize.hpp"
|
||||
#include "config_passthrough.hpp"
|
||||
#include "config_passthrough_io.hpp"
|
||||
#include "config_pid.hpp"
|
||||
#include "config_proxy_ioprio.hpp"
|
||||
#include "config_rename_exdev.hpp"
|
||||
#include "config_set.hpp"
|
||||
#include "config_statfs.hpp"
|
||||
@@ -37,16 +40,19 @@
|
||||
#include "config_xattr.hpp"
|
||||
#include "enum.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "funcs.hpp"
|
||||
#include "fuse_cfg.hpp"
|
||||
#include "fuse_readdir.hpp"
|
||||
#include "policy.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "tofrom_ref.hpp"
|
||||
#include "tofrom_wrapper.hpp"
|
||||
#include "syslog.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -54,13 +60,14 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
typedef ToFromWrapper<uint64_t> ConfigUINT64;
|
||||
typedef ToFromWrapper<int64_t> ConfigS64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef ToFromWrapper<std::filesystem::path> ConfigPath;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
typedef ToFromWrapper<uint64_t> ConfigUINT64;
|
||||
typedef ToFromWrapper<int64_t> ConfigS64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef ToFromWrapper<fs::path> ConfigPath;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
typedef ROToFromWrapper<std::string> ConfigROSTR;
|
||||
|
||||
extern const std::string CONTROLFILE;
|
||||
|
||||
@@ -71,10 +78,30 @@ public:
|
||||
{
|
||||
int err;
|
||||
std::string str;
|
||||
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
typedef std::vector<Err> ErrVec;
|
||||
|
||||
public:
|
||||
ErrVec errs;
|
||||
|
||||
public:
|
||||
class CfgConfigFile : public ToFromString
|
||||
{
|
||||
private:
|
||||
fs::path _cfg_file;
|
||||
int _depth = 0;
|
||||
|
||||
public:
|
||||
CfgConfigFile();
|
||||
|
||||
public:
|
||||
int from_string(const std::string_view s_) final;
|
||||
std::string to_string(void) const final;
|
||||
};
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
@@ -82,9 +109,8 @@ public:
|
||||
Config& operator=(const Config&);
|
||||
|
||||
public:
|
||||
ConfigBOOL allow_idmap;
|
||||
ConfigBOOL async_read;
|
||||
ConfigBOOL auto_cache;
|
||||
ConfigUINT64 minfreespace;
|
||||
Branches branches;
|
||||
ConfigUINT64 branches_mount_timeout;
|
||||
ConfigBOOL branches_mount_timeout_fail;
|
||||
@@ -96,8 +122,11 @@ public:
|
||||
ConfigBOOL cache_readdir;
|
||||
ConfigUINT64 cache_statfs;
|
||||
ConfigBOOL cache_symlinks;
|
||||
ConfigBOOL cache_writeback;
|
||||
Categories category;
|
||||
ConfigBOOL direct_io;
|
||||
CfgConfigFile config_file;
|
||||
TFSRef<int> congestion_threshold;
|
||||
TFSRef<bool> debug;
|
||||
ConfigBOOL direct_io_allow_mmap;
|
||||
ConfigBOOL dropcacheonclose;
|
||||
ConfigBOOL export_support;
|
||||
@@ -106,29 +135,40 @@ public:
|
||||
ConfigSTR fsname;
|
||||
Funcs func;
|
||||
ConfigPageSize fuse_msg_size;
|
||||
TFSRef<s64> gid;
|
||||
GIDCacheExpireTimeout gid_cache_expire_timeout;
|
||||
GIDCacheRemoveTimeout gid_cache_remove_timeout;
|
||||
ConfigBOOL handle_killpriv = true;
|
||||
ConfigBOOL handle_killpriv_v2 = true;
|
||||
ConfigBOOL handle_killpriv;
|
||||
ConfigBOOL handle_killpriv_v2;
|
||||
ConfigBOOL ignorepponrename;
|
||||
InodeCalc inodecalc;
|
||||
ConfigBOOL kernel_cache;
|
||||
ConfigBOOL kernel_permissions_check = true;
|
||||
ConfigBOOL kernel_permissions_check;
|
||||
ConfigBOOL lazy_umount_mountpoint;
|
||||
ConfigBOOL link_cow;
|
||||
LinkEXDEV link_exdev;
|
||||
LogMetrics log_metrics;
|
||||
ConfigPath mountpoint;
|
||||
TFSRef<int> max_background;
|
||||
ConfigUINT64 minfreespace;
|
||||
fs::path mountpoint;
|
||||
TFSRef<fs::path> _mount;
|
||||
TFSRef<fs::path> _mountpoint;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
CfgNoforget noforget;
|
||||
ConfigBOOL nullrw;
|
||||
Passthrough passthrough = Passthrough::ENUM::OFF;
|
||||
ConfigBOOL parallel_direct_writes;
|
||||
PassthroughIO passthrough_io;
|
||||
TFSRef<int> passthrough_max_stack_depth;
|
||||
ConfigGetPid pid;
|
||||
TFSRef<std::string> pin_threads;
|
||||
ConfigBOOL posix_acl;
|
||||
TFSRef<int> process_thread_count;
|
||||
TFSRef<int> process_thread_queue_depth;
|
||||
ProxyIOPrio proxy_ioprio;
|
||||
TFSRef<int> read_thread_count;
|
||||
ConfigUINT64 readahead;
|
||||
FUSE::ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
TFSRef<s64> remember_nodes;
|
||||
RenameEXDEV rename_exdev;
|
||||
ConfigINT scheduling_priority;
|
||||
ConfigBOOL security_capability;
|
||||
@@ -137,14 +177,15 @@ public:
|
||||
StatFSIgnore statfs_ignore;
|
||||
ConfigBOOL symlinkify;
|
||||
ConfigS64 symlinkify_timeout;
|
||||
ConfigINT fuse_read_thread_count;
|
||||
ConfigINT fuse_process_thread_count;
|
||||
ConfigINT fuse_process_thread_queue_depth;
|
||||
ConfigSTR fuse_pin_threads;
|
||||
ConfigSTR version;
|
||||
ConfigBOOL writeback_cache;
|
||||
TFSRef<int> threads;
|
||||
TFSRef<s64> uid;
|
||||
TFSRef<s64> umask;
|
||||
ConfigROSTR version;
|
||||
XAttr xattr;
|
||||
|
||||
private:
|
||||
CfgDummy _dummy;
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
|
||||
@@ -164,17 +205,16 @@ public:
|
||||
|
||||
public:
|
||||
int get(const std::string &key, std::string *val) const;
|
||||
int set_raw(const std::string &key, const std::string &val);
|
||||
int set(const std::string &key, const std::string &val);
|
||||
int set(const std::string &kv);
|
||||
|
||||
public:
|
||||
int from_stream(std::istream &istrm, ErrVec *errs);
|
||||
int from_file(const std::string &filepath, ErrVec *errs);
|
||||
int from_stream(std::istream &istrm);
|
||||
int from_file(const std::string &filepath);
|
||||
|
||||
public:
|
||||
static bool is_rootdir(const char *fusepath);
|
||||
static bool is_ctrl_file(const char *fusepath);
|
||||
static bool is_rootdir(const fs::path &fusepath);
|
||||
static bool is_ctrl_file(const fs::path &fusepath);
|
||||
static bool is_mergerfs_xattr(const char *attrname);
|
||||
static bool is_cmd_xattr(const std::string_view &attrname);
|
||||
static std::string prune_ctrl_xattr(const std::string &s);
|
||||
@@ -182,10 +222,6 @@ public:
|
||||
|
||||
private:
|
||||
Str2TFStrMap _map;
|
||||
|
||||
public:
|
||||
friend class Read;
|
||||
friend class Write;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &s,const Config::ErrVec &ev);
|
||||
|
||||
@@ -26,8 +26,6 @@ CacheFiles::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case CacheFiles::ENUM::LIBFUSE:
|
||||
return "libfuse";
|
||||
case CacheFiles::ENUM::OFF:
|
||||
return "off";
|
||||
case CacheFiles::ENUM::PARTIAL:
|
||||
@@ -47,9 +45,7 @@ template<>
|
||||
int
|
||||
CacheFiles::from_string(const std::string_view s_)
|
||||
{
|
||||
if(s_ == "libfuse")
|
||||
_data = CacheFiles::ENUM::LIBFUSE;
|
||||
ef(s_ == "off")
|
||||
if(s_ == "off")
|
||||
_data = CacheFiles::ENUM::OFF;
|
||||
ef(s_ == "partial")
|
||||
_data = CacheFiles::ENUM::PARTIAL;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
enum class CacheFilesEnum
|
||||
{
|
||||
LIBFUSE,
|
||||
OFF,
|
||||
PARTIAL,
|
||||
FULL,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
class CfgDummy : public ToFromString
|
||||
{
|
||||
public:
|
||||
CfgDummy()
|
||||
{
|
||||
display = false;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int
|
||||
from_string(const std::string_view)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -34,5 +34,5 @@ InodeCalc::to_string(void) const
|
||||
int
|
||||
InodeCalc::from_string(const std::string_view s_)
|
||||
{
|
||||
return fs::inode::set_algo(s_);
|
||||
return fs::inode::set_algo(std::string{s_});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
#include "fuse_cfg.hpp"
|
||||
|
||||
|
||||
class CfgNoforget : public ToFromString
|
||||
{
|
||||
public:
|
||||
int
|
||||
from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
bool b = false;
|
||||
|
||||
rv = str::from(s_,&b);
|
||||
if((b == true) || s_.empty())
|
||||
{
|
||||
fuse_cfg.remember_nodes = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(rv)
|
||||
return rv;
|
||||
|
||||
fuse_cfg.remember_nodes = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
if(fuse_cfg.remember_nodes == -1)
|
||||
return "true";
|
||||
return "false";
|
||||
}
|
||||
};
|
||||
@@ -16,23 +16,23 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config_passthrough.hpp"
|
||||
#include "config_passthrough_io.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
Passthrough::to_string() const
|
||||
PassthroughIO::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case Passthrough::ENUM::OFF:
|
||||
case PassthroughIO::ENUM::OFF:
|
||||
return "off";
|
||||
case Passthrough::ENUM::RO:
|
||||
case PassthroughIO::ENUM::RO:
|
||||
return "ro";
|
||||
case Passthrough::ENUM::WO:
|
||||
case PassthroughIO::ENUM::WO:
|
||||
return "wo";
|
||||
case Passthrough::ENUM::RW:
|
||||
case PassthroughIO::ENUM::RW:
|
||||
return "rw";
|
||||
}
|
||||
|
||||
@@ -41,16 +41,16 @@ Passthrough::to_string() const
|
||||
|
||||
template<>
|
||||
int
|
||||
Passthrough::from_string(const std::string_view s_)
|
||||
PassthroughIO::from_string(const std::string_view s_)
|
||||
{
|
||||
if(s_ == "off")
|
||||
_data = Passthrough::ENUM::OFF;
|
||||
_data = PassthroughIO::ENUM::OFF;
|
||||
ef(s_ == "ro")
|
||||
_data = Passthrough::ENUM::RO;
|
||||
_data = PassthroughIO::ENUM::RO;
|
||||
ef(s_ == "wo")
|
||||
_data = Passthrough::ENUM::WO;
|
||||
_data = PassthroughIO::ENUM::WO;
|
||||
ef(s_ == "rw")
|
||||
_data = Passthrough::ENUM::RW;
|
||||
_data = PassthroughIO::ENUM::RW;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class PassthroughEnum
|
||||
enum class PassthroughIOEnum
|
||||
{
|
||||
OFF = 0,
|
||||
RO,
|
||||
@@ -29,4 +29,4 @@ enum class PassthroughEnum
|
||||
RW
|
||||
};
|
||||
|
||||
typedef Enum<PassthroughEnum> Passthrough;
|
||||
typedef Enum<PassthroughIOEnum> PassthroughIO;
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "config_proxy_ioprio.hpp"
|
||||
|
||||
#include "ioprio.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
|
||||
ProxyIOPrio::ProxyIOPrio(const bool b_)
|
||||
{
|
||||
ioprio::enable(b_);
|
||||
}
|
||||
|
||||
std::string
|
||||
ProxyIOPrio::to_string(void) const
|
||||
{
|
||||
if(ioprio::enabled())
|
||||
return "true";
|
||||
return "false";
|
||||
}
|
||||
|
||||
int
|
||||
ProxyIOPrio::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
bool enable;
|
||||
|
||||
rv = str::from(s_,&enable);
|
||||
if(rv)
|
||||
return rv;
|
||||
|
||||
ioprio::enable(enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
class ProxyIOPrio : public ToFromString
|
||||
{
|
||||
public:
|
||||
ProxyIOPrio(const bool);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string_view) final;
|
||||
};
|
||||
+22
-2
@@ -18,14 +18,34 @@
|
||||
|
||||
#include "fh.hpp"
|
||||
|
||||
#include <string>
|
||||
#include "int_types.h"
|
||||
|
||||
|
||||
class DirInfo : public FH
|
||||
{
|
||||
public:
|
||||
DirInfo(const char *fusepath_)
|
||||
static DirInfo *from_fh(const u64 fh);
|
||||
|
||||
public:
|
||||
u64 to_fh() const;
|
||||
|
||||
public:
|
||||
DirInfo(const fs::path &fusepath_)
|
||||
: FH(fusepath_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
uint64_t
|
||||
DirInfo::to_fh() const
|
||||
{
|
||||
return reinterpret_cast<u64>(this);
|
||||
}
|
||||
|
||||
inline
|
||||
DirInfo*
|
||||
DirInfo::from_fh(const u64 fh_)
|
||||
{
|
||||
return reinterpret_cast<DirInfo*>(fh_);
|
||||
}
|
||||
|
||||
+2
-2
@@ -24,9 +24,9 @@ public:
|
||||
operator=(int v_)
|
||||
{
|
||||
if(!_err.has_value())
|
||||
_err = v_;
|
||||
_err = ((v_ >= 0) ? 0 : v_);
|
||||
else if(v_ >= 0)
|
||||
_err = v_;
|
||||
_err = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
+4
-9
@@ -16,23 +16,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "fs_path.hpp"
|
||||
|
||||
|
||||
class FH
|
||||
{
|
||||
public:
|
||||
FH(const std::string fusepath_)
|
||||
: fusepath(std::move(fusepath_))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FH(const char *fusepath_)
|
||||
FH(const fs::path &fusepath_)
|
||||
: fusepath(fusepath_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
std::string fusepath;
|
||||
fs::path fusepath;
|
||||
};
|
||||
|
||||
+32
-12
@@ -16,10 +16,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fh.hpp"
|
||||
#include "branch.hpp"
|
||||
#include "fh.hpp"
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
#include "int_types.h"
|
||||
@@ -28,24 +28,27 @@
|
||||
class FileInfo : public FH
|
||||
{
|
||||
public:
|
||||
FileInfo(const int fd_,
|
||||
const Branch &branch_,
|
||||
const char *fusepath_,
|
||||
const bool direct_io_)
|
||||
static FileInfo *from_fh(const u64 fh);
|
||||
|
||||
public:
|
||||
FileInfo(const int fd_,
|
||||
const Branch *branch_,
|
||||
const fs::path &fusepath_,
|
||||
const bool direct_io_)
|
||||
: FH(fusepath_),
|
||||
fd(fd_),
|
||||
branch(branch_),
|
||||
branch(*branch_),
|
||||
direct_io(direct_io_)
|
||||
{
|
||||
}
|
||||
|
||||
FileInfo(const int fd_,
|
||||
const Branch *branch_,
|
||||
const char *fusepath_,
|
||||
const bool direct_io_)
|
||||
FileInfo(const int fd_,
|
||||
const Branch &branch_,
|
||||
const fs::path &fusepath_,
|
||||
const bool direct_io_)
|
||||
: FH(fusepath_),
|
||||
fd(fd_),
|
||||
branch(*branch_),
|
||||
branch(branch_),
|
||||
direct_io(direct_io_)
|
||||
{
|
||||
}
|
||||
@@ -58,9 +61,26 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
u64 to_fh() const;
|
||||
|
||||
public:
|
||||
int fd;
|
||||
Branch branch;
|
||||
u32 direct_io:1;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
inline
|
||||
u64
|
||||
FileInfo::to_fh() const
|
||||
{
|
||||
return reinterpret_cast<u64>(this);
|
||||
}
|
||||
|
||||
inline
|
||||
FileInfo*
|
||||
FileInfo::from_fh(const u64 fh_)
|
||||
{
|
||||
return reinterpret_cast<FileInfo*>(fh_);
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
typedef struct fixed_mem_pool_t fixed_mem_pool_t;
|
||||
struct fixed_mem_pool_t
|
||||
{
|
||||
fixed_mem_pool_t *next;
|
||||
};
|
||||
|
||||
template<uint64_t SIZE>
|
||||
class FixedMemPool
|
||||
{
|
||||
public:
|
||||
FixedMemPool()
|
||||
{
|
||||
list.next = NULL;
|
||||
}
|
||||
|
||||
~FixedMemPool()
|
||||
{
|
||||
void *mem;
|
||||
while(!empty())
|
||||
{
|
||||
mem = alloc();
|
||||
::free(mem);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
empty(void)
|
||||
{
|
||||
return (list.next == NULL);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
size(void)
|
||||
{
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
void*
|
||||
alloc(void)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
if(list.next == NULL)
|
||||
return malloc(SIZE);
|
||||
|
||||
rv = (void*)list.next;
|
||||
list.next = list.next->next;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
free(void *mem_)
|
||||
{
|
||||
fixed_mem_pool_t *next;
|
||||
|
||||
next = (fixed_mem_pool_t*)mem_;
|
||||
next->next = list.next;
|
||||
list.next = next;
|
||||
}
|
||||
|
||||
private:
|
||||
fixed_mem_pool_t list;
|
||||
};
|
||||
+1
-1
@@ -187,7 +187,7 @@ str::from(const std::string_view value_,
|
||||
|
||||
int
|
||||
str::from(const std::string_view value_,
|
||||
std::filesystem::path *path_)
|
||||
fs::path *path_)
|
||||
{
|
||||
*path_ = value_;
|
||||
|
||||
|
||||
+3
-2
@@ -18,8 +18,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fs_path.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -32,5 +33,5 @@ namespace str
|
||||
int from(const std::string_view, int64_t *);
|
||||
int from(const std::string_view, std::string *);
|
||||
int from(const std::string_view, const std::string *);
|
||||
int from(const std::string_view, std::filesystem::path *);
|
||||
int from(const std::string_view, fs::path *);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user