Compare commits

...
74 Commits
Author SHA1 Message Date
Antonio SJ Musumeci 3e085beb0e node.cpp 2025-10-27 22:50:26 -05:00
Antonio SJ Musumeci 58abe58ce2 node.cpp 2025-10-27 22:32:28 -05:00
Antonio SJ Musumeci 564bb8c364 node.cpp 2025-10-27 22:32:24 -05:00
Antonio SJ Musumeci 2e3307bac8 node.cpp 2025-10-27 22:32:02 -05:00
Antonio SJ Musumeci d88605de72 node.cpp 2025-10-27 22:31:33 -05:00
Antonio SJ Musumeci 8b5382dde9 node.cpp 2025-10-27 22:30:29 -05:00
Antonio SJ Musumeci 72b9397f67 node.hpp 2025-10-27 22:26:12 -05:00
Antonio SJ Musumeci cda8472120 mergerfs.cpp 2025-10-27 22:25:48 -05:00
Antonio SJ Musumeci 705eb7a5ea mergerfs.cpp 2025-10-27 22:23:08 -05:00
Antonio SJ Musumeci aee1e2d42b fs_open.hpp 2025-10-27 22:17:17 -05:00
Antonio SJ Musumeci 9ac073a1b4 node.cpp 2025-10-27 22:12:51 -05:00
Antonio SJ Musumeci 3394ff525a node.cpp 2025-10-27 22:11:05 -05:00
Antonio SJ Musumeci 05d04b50a1 node.cpp 2025-10-27 22:10:26 -05:00
Antonio SJ Musumeci 42100d985b node.cpp 2025-10-27 22:07:08 -05:00
Antonio SJ Musumeci 7bf3d7617e node.cpp 2025-10-27 21:53:19 -05:00
Antonio SJ Musumeci bd5735dfc9 node.cpp 2025-10-27 21:53:07 -05:00
Antonio SJ Musumeci 09135b0851 node.cpp 2025-10-27 21:52:52 -05:00
Antonio SJ Musumeci 055f6f3335 node.cpp 2025-10-27 21:50:28 -05:00
Antonio SJ Musumeci ddee03452c node.cpp 2025-10-27 21:50:10 -05:00
Antonio SJ Musumeci a7856ae21b node.cpp 2025-10-27 21:49:09 -05:00
Antonio SJ Musumeci ae621d929e node.cpp 2025-10-27 21:49:02 -05:00
Antonio SJ Musumeci eb30c4ac26 node.cpp 2025-10-27 21:48:56 -05:00
Antonio SJ Musumeci db47cdf706 node.cpp 2025-10-27 21:48:26 -05:00
Antonio SJ Musumeci 7d7d50f99c node.cpp 2025-10-27 21:48:22 -05:00
Antonio SJ Musumeci 0f16a14f7f node.cpp 2025-10-27 21:46:09 -05:00
Antonio SJ Musumeci e7f43178c0 node.cpp 2025-10-27 21:46:04 -05:00
Antonio SJ Musumeci 75d6234e0c node.cpp 2025-10-27 21:45:54 -05:00
Antonio SJ Musumeci 93c9400cee node.cpp 2025-10-27 21:44:17 -05:00
Antonio SJ Musumeci efb550dac6 node.cpp 2025-10-27 21:43:59 -05:00
Antonio SJ Musumeci ea11421ef3 node.cpp 2025-10-27 21:43:46 -05:00
Antonio SJ Musumeci 5f38796425 node.cpp 2025-10-27 21:43:29 -05:00
Antonio SJ Musumeci 71355aeede node.cpp 2025-10-27 21:42:44 -05:00
Antonio SJ Musumeci 36aad5ab59 node.cpp 2025-10-27 21:41:56 -05:00
Antonio SJ Musumeci 18ac5a9b55 fs_open.hpp 2025-10-27 21:40:53 -05:00
Antonio SJ Musumeci 33b587ffac node.cpp 2025-10-27 21:40:22 -05:00
Antonio SJ Musumeci 66b38d5e38 node.cpp 2025-10-27 21:40:18 -05:00
Antonio SJ Musumeci 79f18ad09e node.cpp 2025-10-27 21:39:29 -05:00
Antonio SJ Musumeci 3204d74d25 node.cpp 2025-10-27 21:38:20 -05:00
Antonio SJ Musumeci 1e45fa4d31 node.cpp 2025-10-27 21:38:13 -05:00
Antonio SJ Musumeci e55a5aea49 node.cpp 2025-10-27 21:38:06 -05:00
Antonio SJ Musumeci f3997fc8a8 node.cpp 2025-10-27 21:37:59 -05:00
Antonio SJ Musumeci 1745d7c10a node.cpp 2025-10-27 21:00:38 -05:00
Antonio SJ Musumeci 38c8e09913 node.cpp 2025-10-27 21:00:32 -05:00
Antonio SJ Musumeci dd8ecdd792 node.cpp 2025-10-27 21:00:21 -05:00
Antonio SJ Musumeci 04b61c8b29 node.cpp 2025-10-27 20:58:06 -05:00
Antonio SJ Musumeci e9bfa96f26 node.cpp 2025-10-27 08:56:42 -05:00
Antonio SJ Musumeci 95e62e6a98 node.cpp 2025-10-27 08:56:42 -05:00
Antonio SJ Musumeci c33ef49cdf Remove pipe_key from libfuse 2025-10-27 08:56:42 -05:00
trapexit cfaad60275 Misc updates to arg parsing (#1554)
Ignore "defaults" and add a number of more modern VFS mount switches
2025-10-26 21:05:05 -05:00
trapexit 077484323c Fix variable names of fuse-msg-size and posix-acl (#1553) 2025-10-25 17:00:16 -05:00
trapexit 700fcfccf5 Update fuse-msg-size docs (#1552) 2025-10-24 23:34:28 -05:00
trapexit 4b4801add6 Config options docs updates (#1551) 2025-10-24 22:31:04 -05:00
trapexit 741128cb23 Fix lack of include for fmt (#1550) 2025-10-24 21:00:15 -05:00
trapexit 3bee0f0b57 Misc docs updates (#1549) 2025-10-24 20:47:33 -05:00
trapexit 727c522fb8 Rework config, centralize fuse config (#1547) 2025-10-24 08:20:29 -05:00
trapexit 00e5467e99 Misc docs updates (#1544) 2025-10-13 11:44:53 -05:00
trapexit 0371f03def Rework how fuse request context is handled (#1543) 2025-10-12 23:21:36 -05:00
trapexit d18c273293 Allow setting of passthrough max-stack-depth (#1542) 2025-10-07 21:52:13 -05:00
trapexit 0f24bb29ac Make allow-idmap optional (default: false) (#1541)
This is just to correct default behavior for now. More investigation
is necessary to properly support idmapping.
2025-10-06 14:46:58 -05:00
trapexit 3cb35a3a65 Fix rename path generation (#1540) 2025-10-01 17:47:13 -05:00
trapexit 25b0bc72df Fix dirent64::namelen calculation (#1538)
Worked fine on my machine where padding seemed to be all zeroed out
but that doesn't appear to be the case elsewhere.

Could try
https://codeberg.org/jbruchon/libjodycode/src/branch/master/dir.c#L92-L110
but needs to be tested.
2025-09-30 11:25:19 -05:00
trapexit e6fcebe4f3 Misc build fixes (#1537) 2025-09-29 14:05:07 -05:00
trapexit 46f85c0f6d Misc fixes, mostly for FreeBSD (#1536) 2025-09-28 23:17:51 -05:00
trapexit 7a9c3b7eb2 Remove need for and use of ioprio header (#1535) 2025-09-28 00:01:26 -05:00
trapexit 569a151da2 Add ability to proxy ioprio of client apps (#1534) 2025-09-26 11:36:56 -05:00
trapexit 2f3e807e9d Add getdents based readdir functions for Linux (#1533) 2025-09-23 23:51:21 -05:00
trapexit 95ab37fdd2 Fix typo in visualization and add new vid to docs (#1532) 2025-09-22 16:23:50 -05:00
trapexit 1707dc27e3 Misc doc updates (#1531) 2025-09-21 14:25:24 -05:00
trapexit c4126a0796 Add script to generate docs from scratch and push to github (#1530) 2025-09-21 13:04:55 -05:00
pjv 6acef3a638 Fix typo in moveonenospc.md (#1527)
typo
2025-09-21 11:42:15 -05:00
trapexit b3656a0243 Fix broken doc links (#1525) 2025-09-19 19:25:31 -05:00
trapexit 41b40b94fa Misc docs updates (#1524) 2025-09-19 19:20:28 -05:00
trapexit ca59ae53a5 idmap mount support (#1523)
* Add idmap mount support

* Further replacement of std::filesystem::path to fs::path

* Update docs regarding idmap support
2025-09-18 00:07:28 -05:00
trapexit 8c649d33ee Start using std::filesystem::path for fusepath (#1522) 2025-09-17 22:51:16 -05:00
318 changed files with 6542 additions and 7020 deletions
+8 -4
View File
@@ -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 ?=
+6 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+25
View File
@@ -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
View File
@@ -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
+38
View File
@@ -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;
+2 -56
View File
@@ -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_ */
-31
View File
@@ -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);
+12
View File
@@ -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;
};
-80
View File
@@ -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
+48
View File
@@ -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
-14
View File
@@ -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;
};
+18
View File
@@ -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 *);
+16
View File
@@ -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;
};
-16
View File
@@ -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();
+3 -3
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -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);
}
-73
View File
@@ -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_;
}
-406
View File
@@ -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);
}
+160
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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);
+7 -8
View File
@@ -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();
File diff suppressed because it is too large Load Diff
+24 -12
View File
@@ -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_);
}
+36
View File
@@ -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>
+54 -102
View File
@@ -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;
}
-1
View File
@@ -7,7 +7,6 @@
*/
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_opt.h"
#include <sys/stat.h>
+117 -131
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+8 -8
View File
@@ -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
+16 -14
View File
@@ -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).
+25 -5
View File
@@ -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
+1 -1
View File
@@ -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
+39 -25
View File
@@ -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
+28 -29
View File
@@ -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
+53
View File
@@ -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%).
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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)
+11
View File
@@ -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.
+65 -27
View File
@@ -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.
+1 -1
View File
@@ -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
+31 -15
View File
@@ -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
View File
@@ -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.
+47 -30
View File
@@ -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
+2 -1
View File
@@ -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
+5 -6
View File
@@ -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
+3
View File
@@ -1,5 +1,8 @@
# Project Comparisons
There are no solutions, only trade-offs.
## mhddfs
* [https://romanrm.net/mhddfs](https://romanrm.net/mhddfs)
+30 -17
View File
@@ -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]
+4
View File
@@ -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
+23 -4
View File
@@ -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
+1 -1
View File
@@ -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
+10
View File
@@ -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
+1
View File
@@ -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)
+3 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
+25
View File
@@ -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}"
+1
View File
@@ -64,6 +64,7 @@ Branch::to_string(void) const
break;
}
if(std::holds_alternative<u64>(_minfreespace))
{
rv += ',';
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"] = &parallel_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"] = &parallel_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
View File
@@ -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);
+1 -5
View File
@@ -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;
-1
View File
@@ -23,7 +23,6 @@
enum class CacheFilesEnum
{
LIBFUSE,
OFF,
PARTIAL,
FULL,
+24
View File
@@ -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;
}
};
+1 -1
View File
@@ -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_});
}
+40
View File
@@ -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;
+33
View File
@@ -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;
}
+13
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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_);
}
-89
View File
@@ -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
View File
@@ -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
View File
@@ -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