mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-19 16:16:48 +00:00
Compare commits
120
Commits
read-failover2
...
2.33.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9ab18282f | ||
|
|
14c2ff9ab8 | ||
|
|
77e6f400b5 | ||
|
|
b95ff8ba3a | ||
|
|
4bf8738e5c | ||
|
|
3f060f4512 | ||
|
|
c4c35a9ad0 | ||
|
|
c9a935835f | ||
|
|
015002ebd9 | ||
|
|
9ca10b2413 | ||
|
|
2d5c2c2b6e | ||
|
|
e3ab739a56 | ||
|
|
c784d06029 | ||
|
|
58803f4845 | ||
|
|
f159fe61f9 | ||
|
|
c43b464d6c | ||
|
|
34c684a4f5 | ||
|
|
e256c88e4c | ||
|
|
518b4a6b85 | ||
|
|
ee8532c70d | ||
|
|
c58da1ab9c | ||
|
|
68b3026264 | ||
|
|
810c5a40f7 | ||
|
|
18dead4d86 | ||
|
|
2082357d3a | ||
|
|
68719da828 | ||
|
|
31fd1ec82e | ||
|
|
c3845097ee | ||
|
|
d76934ee9d | ||
|
|
5f737cb7bf | ||
|
|
50ede1ba97 | ||
|
|
930dad31de | ||
|
|
88b91161e0 | ||
|
|
8150957a01 | ||
|
|
fee97efafb | ||
|
|
80856cf7ac | ||
|
|
c929781d08 | ||
|
|
313aa2a1d3 | ||
|
|
021ce3d97c | ||
|
|
4ea0de3ef2 | ||
|
|
6b5c484fbf | ||
|
|
b165332486 | ||
|
|
f598d3bc38 | ||
|
|
4daa9e4eb9 | ||
|
|
122f9c1d5d | ||
|
|
2da5cda4b3 | ||
|
|
5263a65dcf | ||
|
|
1255d3d1b2 | ||
|
|
8def16a811 | ||
|
|
7d1683b607 | ||
|
|
43a6d66e3c | ||
|
|
8b33f9a766 | ||
|
|
df721eb253 | ||
|
|
0547517a99 | ||
|
|
54e85007a2 | ||
|
|
6e53f3b698 | ||
|
|
1aaf742c13 | ||
|
|
3754e20049 | ||
|
|
b4c031d1f0 | ||
|
|
3b8d495c43 | ||
|
|
61c2187c14 | ||
|
|
192e4a4af8 | ||
|
|
749f0290e9 | ||
|
|
7013ff9aeb | ||
|
|
3ce0dc56e4 | ||
|
|
8e47bf4946 | ||
|
|
098f353bf1 | ||
|
|
859b4da3dc | ||
|
|
80f30999fa | ||
|
|
3fdd322a5c | ||
|
|
c40f42e57e | ||
|
|
18e87980a4 | ||
|
|
44efbaabe6 | ||
|
|
a02f3a6856 | ||
|
|
9573a053d6 | ||
|
|
9a5d691619 | ||
|
|
495afeffb1 | ||
|
|
4acc1defe2 | ||
|
|
58b4eb9089 | ||
|
|
ca4b4c6d35 | ||
|
|
9a90609428 | ||
|
|
3e691149f5 | ||
|
|
8adebc9489 | ||
|
|
50daf84ec0 | ||
|
|
d337574665 | ||
|
|
39d98f1eac | ||
|
|
cbea2ba91b | ||
|
|
f6e37672b8 | ||
|
|
55bd9a06b1 | ||
|
|
6a9f7e6afc | ||
|
|
f2158d3e66 | ||
|
|
3bc189cfc8 | ||
|
|
d0c5ff7f87 | ||
|
|
dfb544fed8 | ||
|
|
ae893ca051 | ||
|
|
538467b86d | ||
|
|
46de43d4ad | ||
|
|
3808e2683e | ||
|
|
52342a76c1 | ||
|
|
e9e17baf5e | ||
|
|
2e4c6c5fd1 | ||
|
|
f50812a9ce | ||
|
|
861edff49a | ||
|
|
5e08357f0e | ||
|
|
411ee36c90 | ||
|
|
44a98910ba | ||
|
|
8a335d8a6e | ||
|
|
3900543970 | ||
|
|
2e34c7598a | ||
|
|
7edd3c6cf3 | ||
|
|
ddf0075938 | ||
|
|
1b26f4908e | ||
|
|
2d8fab534a | ||
|
|
c3fffefbc1 | ||
|
|
eaa2593a4b | ||
|
|
7e583e3f48 | ||
|
|
2ad4891617 | ||
|
|
8990e24c0f | ||
|
|
88b80234c7 | ||
|
|
f6e37c57fe |
+190
-8
@@ -1,3 +1,13 @@
|
||||
freebsd_task:
|
||||
name: "freebsd:11.4"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-11-4
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
name: "freebsd:12.1"
|
||||
freebsd_instance:
|
||||
@@ -8,6 +18,27 @@ freebsd_task:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
name: "freebsd:12.2"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-2
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
name: "freebsd:13.0"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-0
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
|
||||
#macos_task:
|
||||
# osx_instance:
|
||||
# image: catalina-base
|
||||
@@ -27,16 +58,59 @@ linux_task:
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "centos:6"
|
||||
name: "alpine:3.12"
|
||||
container:
|
||||
image: centos:6
|
||||
image: alpine:3.12
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.13"
|
||||
container:
|
||||
image: alpine:3.13
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.14"
|
||||
container:
|
||||
image: alpine:3.14
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:latest"
|
||||
container:
|
||||
image: alpine:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine-arm:latest"
|
||||
arm_container:
|
||||
image: alpine:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "centos:7"
|
||||
@@ -98,6 +172,54 @@ linux_task:
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:33"
|
||||
container:
|
||||
image: fedora:33
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:34"
|
||||
container:
|
||||
image: fedora:34
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:latest"
|
||||
container:
|
||||
image: fedora:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora-arm:latest"
|
||||
arm_container:
|
||||
image: fedora:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:14.04"
|
||||
container:
|
||||
@@ -144,9 +266,9 @@ linux_task:
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:19.10"
|
||||
name: "ubuntu:20.04"
|
||||
container:
|
||||
image: ubuntu:19.10
|
||||
image: ubuntu:20.04
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
@@ -159,9 +281,24 @@ linux_task:
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:20.04"
|
||||
name: "ubuntu:latest"
|
||||
container:
|
||||
image: ubuntu:20.04
|
||||
image: ubuntu:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu-arm:latest"
|
||||
arm_container:
|
||||
image: ubuntu:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
@@ -232,3 +369,48 @@ linux_task:
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:11"
|
||||
container:
|
||||
image: debian:11
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:latest"
|
||||
container:
|
||||
image: debian:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian-arm:latest"
|
||||
arm_container:
|
||||
image: debian:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
+2
-2
@@ -2,6 +2,6 @@
|
||||
|
||||
github: [trapexit]
|
||||
patreon: trapexit
|
||||
open_collective: trapexit
|
||||
custom: ['https://paypal.me/trapexit','https://buymeacoff.ee/trapexit']
|
||||
ko_fi: trapexit
|
||||
custom: ['https://paypal.me/trapexit','https://www.subscribestar.com/trapexit']
|
||||
open_collective: trapexit
|
||||
|
||||
@@ -8,27 +8,40 @@ assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. Please do not file bugs for unreleased versions unless you're a tester or sure it's not a work in progress. The master branch is **not** to be considered in a production state.
|
||||
|
||||
A clear and concise description of the unexpected behavior. Please be sure to use latest release of mergerfs to ensure the issue still exists.
|
||||
|
||||
The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
|
||||
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
Steps to reproduce the behavior. List **all** steps to reproduce. **All** settings.
|
||||
|
||||
Please simplify the reproduction as much as possible.
|
||||
- Unless it is dependenat on multiple branches, use a single branch
|
||||
- Use basic tooling if possible (touch,truncate,rm,rmdir,ln,etc.)
|
||||
- Use standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.)
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
A clear and concise description of the expected behavior.
|
||||
|
||||
|
||||
**System information:**
|
||||
|
||||
- OS, kernel version: `uname -a`
|
||||
- mergerfs version: `mergerfs -V`
|
||||
- mergerfs settings
|
||||
- List of drives, filesystems, & sizes: `df -h`
|
||||
- A strace of the application having a problem: `strace -f -o /tmp/app.strace.txt <cmd>` or `strace -f -p <appPID> -o /tmp/app.strace.txt`
|
||||
- strace of mergerfs while app tried to do it's thing: `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
- List of drives, filesystems, & sizes:
|
||||
- `df -h`
|
||||
- `lsblk -f`
|
||||
- A strace of the application having a problem:
|
||||
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
|
||||
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
|
||||
- strace of mergerfs while app tried to do it's thing:
|
||||
- `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: precise
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: precise
|
||||
compiler: clang
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: clang
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang
|
||||
sudo: required
|
||||
|
||||
script:
|
||||
- sudo -E apt-get install fakeroot
|
||||
- sudo -E make install-build-pkgs
|
||||
- make -j$(($(nproc) + 1))
|
||||
- make deb
|
||||
- sudo -E dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Copyright (c) 2021, 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
|
||||
|
||||
@@ -40,7 +40,7 @@ USE_XATTR = 1
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
@@ -58,13 +58,20 @@ LTO_FLAGS :=
|
||||
endif
|
||||
|
||||
SRC = $(wildcard src/*.cpp)
|
||||
OBJS = $(SRC:src/%.cpp=build/%.o)
|
||||
DEPS = $(SRC:src/%.cpp=build/%.d)
|
||||
OBJS = $(SRC:src/%.cpp=build/.src/%.o)
|
||||
DEPS = $(SRC:src/%.cpp=build/.src/%.d)
|
||||
|
||||
TESTS = $(wildcard tests/*.cpp)
|
||||
TESTS_OBJS = $(filter-out build/.src/mergerfs.o,$(OBJS))
|
||||
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.tests/%.o)
|
||||
TESTS_DEPS = $(TESTS:tests/%.cpp=build/.tests/%.d)
|
||||
TESTS_DEPS += $(DEPS)
|
||||
|
||||
MANPAGE = mergerfs.1
|
||||
CXXFLAGS ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++0x \
|
||||
-std=c++11 \
|
||||
$(STATIC_FLAGS) \
|
||||
$(LTO_FLAGS) \
|
||||
-Wall \
|
||||
@@ -77,6 +84,9 @@ FUSE_FLAGS = \
|
||||
MFS_FLAGS = \
|
||||
-DUSE_XATTR=$(USE_XATTR) \
|
||||
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
|
||||
TESTS_FLAGS = \
|
||||
-Isrc \
|
||||
-DTESTS
|
||||
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
@@ -110,11 +120,19 @@ help:
|
||||
objects: version build/stamp
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
tests-objects:
|
||||
$(MAKE) $(TESTS_OBJS)
|
||||
|
||||
build/mergerfs: libfuse objects
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
build/tests: build/mergerfs tests-objects
|
||||
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
mergerfs: build/mergerfs
|
||||
|
||||
tests: build/tests
|
||||
|
||||
changelog:
|
||||
ifeq ($(GIT_REPO),1)
|
||||
$(GIT2DEBCL) --name mergerfs > ChangeLog
|
||||
@@ -127,12 +145,16 @@ version:
|
||||
tools/update-version
|
||||
|
||||
build/stamp:
|
||||
$(MKDIR) -p build
|
||||
$(MKDIR) -p build/.src build/.tests
|
||||
$(TOUCH) $@
|
||||
|
||||
build/%.o: src/%.cpp
|
||||
build/.src/%.o: src/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
build/.tests/%.o: tests/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean: rpm-clean
|
||||
$(RM) -rf build
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
% mergerfs(1) mergerfs user manual
|
||||
% Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
% 2020-08-30
|
||||
% 2021-10-25
|
||||
|
||||
# NAME
|
||||
|
||||
@@ -19,19 +19,19 @@ mergerfs -o<options> <branches> <mountpoint>
|
||||
|
||||
# FEATURES
|
||||
|
||||
* Runs in userspace (FUSE)
|
||||
* Configurable behaviors / file placement
|
||||
* Ability to add or remove filesystems at will
|
||||
* Resistance to individual filesystem failure
|
||||
* Support for extended attributes (xattrs)
|
||||
* Support for file attributes (chattr)
|
||||
* Runtime configurable (via xattrs)
|
||||
* Safe to run as root
|
||||
* Opportunistic credential caching
|
||||
* Works with heterogeneous filesystem types
|
||||
* Handling of writes to full drives (transparently move file to drive with capacity)
|
||||
* Handles pool of read-only and read/write drives
|
||||
* Can turn read-only files into symlinks to underlying file
|
||||
* Moving of file when filesystem runs out of space while writing
|
||||
* Ignore read-only filesystems when creating files
|
||||
* Turn read-only files into symlinks to underlying file
|
||||
* Hard link copy-on-write / CoW
|
||||
* supports POSIX ACLs
|
||||
* Support for POSIX ACLs
|
||||
* Misc other things
|
||||
|
||||
|
||||
# HOW IT WORKS
|
||||
@@ -59,7 +59,7 @@ A + B = C
|
||||
+-- file6
|
||||
```
|
||||
|
||||
mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **not** split data across drives. It is not RAID0 / striping. It is simply a union.
|
||||
mergerfs does **NOT** support the copy-on-write (CoW) or whiteout behaviors found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **NOT** split data across drives. It is not RAID0 / striping. It is simply a union of other filesystems.
|
||||
|
||||
|
||||
# TERMINOLOGY
|
||||
@@ -67,9 +67,9 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
|
||||
* branch: A base path used in the pool.
|
||||
* pool: The mergerfs mount. The union of the branches.
|
||||
* relative path: The path in the pool relative to the branch and mount.
|
||||
* function: A filesystem call (open, unlink, create, getattr, rmdir, etc.)
|
||||
* category: A collection of functions based on basic behavior (action, create, search).
|
||||
* policy: The algorithm used to select a file when performing a function.
|
||||
* function: A filesystem call (open, unlink, create, getattr, etc.)
|
||||
* category: A collection of functions (action, create, search).
|
||||
* path preservation: Aspect of some policies which includes checking the path for which a file would be created.
|
||||
|
||||
|
||||
@@ -77,31 +77,34 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
|
||||
|
||||
If you don't already know that you have a special use case then just start with one of the following option sets.
|
||||
|
||||
#### You need `mmap` (used by rtorrent and many sqlite3 base software)
|
||||
|
||||
`allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs`
|
||||
|
||||
#### You don't need `mmap`
|
||||
|
||||
`use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs`
|
||||
`allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs`
|
||||
|
||||
#### You do need `mmap` (used by rtorrent and some other programs)
|
||||
|
||||
`use_ino,cache.files=partial,dropcacheonclose=true,allow_other,category.create=mfs`
|
||||
|
||||
See the mergerfs [wiki for real world deployments](https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments) for comparisons / ideas.
|
||||
|
||||
|
||||
# OPTIONS
|
||||
|
||||
These options are the same regardless you use them with the `mergerfs` commandline program, used in fstab, or in a config file.
|
||||
|
||||
### mount options
|
||||
|
||||
* **config**: Path to a config file. Same arguments as below in key=val format.
|
||||
* **config**: Path to a config file. Same arguments as below in key=val / ini style format.
|
||||
* **branches**: Colon delimited list of branches.
|
||||
* **allow_other**: A libfuse option which allows users besides the one which ran mergerfs to see the filesystem. This is required for most use-cases.
|
||||
* **minfreespace=SIZE**: The minimum space value used for creation policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
|
||||
* **minfreespace=SIZE**: The minimum space value used for creation policies. Can be overridden by branch specific option. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
|
||||
* **moveonenospc=BOOL|POLICY**: When enabled if a **write** fails with **ENOSPC** (no space left on device) or **EDQUOT** (disk quota exceeded) the policy selected will run to find a new location for the file. An attempt to move the file to that branch will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false, true = mfs)
|
||||
* **use_ino**: Causes mergerfs to supply file/directory inodes rather than libfuse. While not a default it is recommended it be enabled so that linked files share the same inode value.
|
||||
* **inodecalc=passthrough|path-hash|devino-hash|hybrid-hash**: Selects the inode calculation algorithm. (default: hybrid-hash)
|
||||
* **dropcacheonclose=BOOL**: When a file is requested to be closed call `posix_fadvise` on it first to instruct the kernel that we no longer need the data and it can drop its cache. Recommended when **cache.files=partial|full|auto-full** to limit double caching. (default: false)
|
||||
* **symlinkify=BOOL**: When enabled and a file is not 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=INT**: Time to wait, in seconds, to activate the **symlinkify** behavior. (default: 3600)
|
||||
* **symlinkify_timeout=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 mergerfs. (default: false)
|
||||
* **ignorepponrename=BOOL**: Ignore path preserving on rename. Typically rename and link act differently depending on the policy of `create` (read below). Enabling this will cause rename and link to always use the non-path preserving behavior. This means files, when renamed or linked, will stay on the same drive. (default: false)
|
||||
* **security_capability=BOOL**: If false return ENOATTR when xattr security.capability is queried. (default: true)
|
||||
@@ -110,18 +113,23 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
|
||||
* **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base)
|
||||
* **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)
|
||||
* **nfsopenhack=off|git|all**: A workaround for exporting mergerfs over NFS where there are issues with creating files for write while setting the mode to read-only. (default: off)
|
||||
* **follow-symlinks=never|directory|regular|all**: Turns symlinks into what they point to. (default: never)
|
||||
* **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**: When a link fails with EXDEV optionally create a symlink to the file instead.
|
||||
* **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename fails with EXDEV optionally move the file to a special directory and symlink to it.
|
||||
* **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 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=INT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
|
||||
* **fuse_msg_size=UINT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
|
||||
* **threads=INT**: Number of threads to use in multithreaded mode. When set to zero it will attempt to discover and use the number of logical cores. If the lookup fails it will fall back to using 4. 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 least 1 thread. NOTE: higher number of threads increases parallelism but usually decreases throughput. (default: 0)
|
||||
* **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=POLICY**: Sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest**
|
||||
* **category.CATEGORY=POLICY**: Sets policy of all FUSE functions in the provided category. See POLICIES section for defaults. Example: **category.create=mfs**
|
||||
* **cache.open=INT**: 'open' policy cache timeout in seconds. (default: 0)
|
||||
* **cache.statfs=INT**: 'statfs' cache timeout in seconds. (default: 0)
|
||||
* **cache.attr=INT**: File attribute cache timeout in seconds. (default: 1)
|
||||
* **cache.entry=INT**: File name lookup cache timeout in seconds. (default: 1)
|
||||
* **cache.negative_entry=INT**: Negative file name lookup cache timeout in seconds. (default: 0)
|
||||
* **category.action=POLICY**: Sets policy of all FUSE functions in the action category. (default: epall)
|
||||
* **category.create=POLICY**: Sets policy of all FUSE functions in the create category. (default: epmfs)
|
||||
* **category.search=POLICY**: Sets policy of all FUSE functions in the search category. (default: ff)
|
||||
* **cache.open=UINT**: 'open' policy cache timeout in seconds. (default: 0)
|
||||
* **cache.statfs=UINT**: 'statfs' cache timeout in seconds. (default: 0)
|
||||
* **cache.attr=UINT**: File attribute cache timeout in seconds. (default: 1)
|
||||
* **cache.entry=UINT**: File name lookup cache timeout in seconds. (default: 1)
|
||||
* **cache.negative_entry=UINT**: Negative file name lookup cache timeout in seconds. (default: 0)
|
||||
* **cache.files=libfuse|off|partial|full|auto-full**: File page caching mode (default: libfuse)
|
||||
* **cache.writeback=BOOL**: Enable kernel writeback caching (default: false)
|
||||
* **cache.symlinks=BOOL**: Cache symlinks (if supported by kernel) (default: false)
|
||||
@@ -139,21 +147,38 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
|
||||
#### Value Types
|
||||
|
||||
* BOOL = 'true' | 'false'
|
||||
* INT = [0,MAX_INT]
|
||||
* INT = [MIN_INT,MAX_INT]
|
||||
* UINT = [0,MAX_INT]
|
||||
* SIZE = 'NNM'; NN = INT, M = 'K' | 'M' | 'G' | 'T'
|
||||
* STR = string
|
||||
* FUNC = FUSE function
|
||||
* CATEGORY = FUSE function category
|
||||
* FUNC = filesystem function
|
||||
* CATEGORY = function category
|
||||
* POLICY = mergerfs function policy
|
||||
|
||||
|
||||
### branches
|
||||
|
||||
The 'branches' (formerly 'srcmounts') argument is a colon (':') delimited list of paths to be pooled together. It does not matter if the paths are on the same or different drives nor does it matter the filesystem. Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
|
||||
The 'branches' argument is a colon (':') delimited list of paths to be pooled together. It does not matter if the paths are on the same or different drives nor does it matter the filesystem (within reason). Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
|
||||
|
||||
Branches currently have two options which can be set. A type which impacts whether or not the branch is included in a policy calculation and a individual minfreespace value. The values are set by prepending an `=` at the end of a branch designation and using commas as delimiters. Example: /mnt/drive=RW,1234
|
||||
|
||||
|
||||
#### branch type
|
||||
|
||||
* RW: (read/write) - Default behavior. Will be eligible in all policy categories.
|
||||
* RO: (read-only) - Will be excluded from `create` and `action` policies. Same as a read-only mounted filesystem would be (though faster to process).
|
||||
* NC: (no-create) - Will be excluded from `create` policies. You can't create on that branch but you can change or delete.
|
||||
|
||||
|
||||
#### minfreespace
|
||||
|
||||
Same purpose as the global option but specific to the branch. If not set the global value is used.
|
||||
|
||||
|
||||
#### globbing
|
||||
|
||||
To make it easier to include multiple branches mergerfs supports [globbing](http://linux.die.net/man/7/glob). **The globbing tokens MUST be escaped when using via the shell else the shell itself will apply the glob itself.**
|
||||
|
||||
Each branch can have a suffix of `=RW` (read / write), `=RO` (read-only), or `=NC` (no create). These suffixes work with globs as well and will apply to each path found. `RW` is the default behavior and those paths will be eligible for all policy categories. `RO` will exclude those paths from `create` and `action` policies (just as a filesystem being mounted `ro` would). `NC` will exclude those paths from `create` policies (you can't create but you can change / delete).
|
||||
|
||||
```
|
||||
# mergerfs -o allow_other,use_ino /mnt/disk\*:/mnt/cdrom /media/drives
|
||||
@@ -168,7 +193,7 @@ To have the pool mounted at boot or otherwise accessible from related tools use
|
||||
/mnt/disk*:/mnt/cdrom /mnt/pool fuse.mergerfs allow_other,use_ino 0 0
|
||||
```
|
||||
|
||||
**NOTE:** the globbing is done at mount or xattr update time (see below). If a new directory is added matching the glob after the fact it will not be automatically included.
|
||||
**NOTE:** the globbing is done at mount or when updated using the runtime API. If a new directory is added matching the glob after the fact it will not be automatically included.
|
||||
|
||||
**NOTE:** for mounting via **fstab** to work you must have **mount.fuse** installed. For Ubuntu/Debian it is included in the **fuse** package.
|
||||
|
||||
@@ -218,6 +243,54 @@ In Linux 4.20 a new feature was added allowing the negotiation of the max messag
|
||||
Since there should be no downsides to increasing `fuse_msg_size` / `max_pages`, outside a minor bump in RAM usage due to larger message buffers, mergerfs defaults the value to 256. On kernels before 4.20 the value has no effect. The reason the value is configurable is to enable experimentation and benchmarking. See the BENCHMARKING section for examples.
|
||||
|
||||
|
||||
### follow-symlinks
|
||||
|
||||
This feature, when enabled, will cause symlinks to be interpreted by mergerfs as their target (depending on the mode).
|
||||
|
||||
When there is a getattr/stat request for a file mergerfs will check if the file is a symlink and depending on the `follow-symlinks` setting will replace the information about the symlink with that of that which it points to.
|
||||
|
||||
When unlink'ing or rmdir'ing the followed symlink it will remove the symlink itself and not that which it points to.
|
||||
|
||||
* never: Behave as normal. Symlinks are treated as such.
|
||||
* directory: Resolve symlinks only which point to directories.
|
||||
* regular: Resolve symlinks only which point to regular files.
|
||||
* all: Resolve all symlinks to that which they point to.
|
||||
|
||||
Symlinks which do not point to anything are left as is.
|
||||
|
||||
WARNING: This feature works but there might be edge cases yet found. If you find any odd behaviors please file a ticket on [github](https://github.com/trapexit/mergerfs/issues).
|
||||
|
||||
|
||||
### link-exdev
|
||||
|
||||
If using path preservation and a `link` fails with EXDEV make a call to `symlink` where the `target` is the `oldlink` and the `linkpath` is the `newpath`. The `target` value is determined by the value of `link-exdev`.
|
||||
|
||||
* passthrough: Return EXDEV as normal.
|
||||
* rel-symlink: A relative path from the `newpath`.
|
||||
* abs-base-symlink: A absolute value using the underlying branch.
|
||||
* abs-pool-symlink: A absolute value using the mergerfs mount point.
|
||||
|
||||
NOTE: It is possible that some applications check the file they link. In those cases it is possible it will error or complain.
|
||||
|
||||
|
||||
### rename-exdev
|
||||
|
||||
If using path preservation and a `rename` fails with EXDEV:
|
||||
|
||||
1. Move file from **/branch/a/b/c** to **/branch/.mergerfs_rename_exdev/a/b/c**.
|
||||
2. symlink the rename's `newpath` to the moved file.
|
||||
|
||||
The `target` value is determined by the value of `rename-exdev`.
|
||||
|
||||
* passthrough: Return EXDEV as normal.
|
||||
* rel-symlink: A relative path from the `newpath`.
|
||||
* abs-symlink: A absolute value using the mergerfs mount point.
|
||||
|
||||
NOTE: It is possible that some applications check the file they rename. In those cases it is possible it will error or complain.
|
||||
|
||||
NOTE: The reason `abs-symlink` is not split into two like `link-exdev` is due to the complexities in managing absolute base symlinks when multiple `oldpaths` exist.
|
||||
|
||||
|
||||
### symlinkify
|
||||
|
||||
Due to the levels of indirection introduced by mergerfs and the underlying technology FUSE there can be varying levels of performance degradation. This feature will turn non-directories which are not writable into symlinks to the original file found by the `readlink` policy after the mtime and ctime are older than the timeout.
|
||||
@@ -254,25 +327,47 @@ This hack addresses the issue where the creation of a file with a read-only mode
|
||||
Even though it's a more niche situation this hack breaks normal security and behavior and as such is `off` by default. If set to `git` it will only perform the hack when the path in question includes `/.git/`. `all` will result it it applying anytime a readonly file which is empty is opened for writing.
|
||||
|
||||
|
||||
# FUNCTIONS / POLICIES / CATEGORIES
|
||||
# FUNCTIONS, CATEGORIES and POLICIES
|
||||
|
||||
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. For ease of configuration in mergerfs most of the core functions are grouped into 3 categories: **action**, **create**, and **search**. These functions and categories can be assigned a policy which dictates which underlying branch/file/directory is chosen when performing that behavior. Any policy can be assigned to a function or category though some may not be very useful in practice. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` if there were more than one copy of the file.
|
||||
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. For ease of configuration in mergerfs most of 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.
|
||||
|
||||
Some functions, listed in the category `N/A` below, can not be assigned the normal policies. All functions which work on file handles use the handle which was acquired by `open` or `create`. `readdir` has no real need for a policy given the purpose is merely to return a list of entries in a directory. `statfs`'s behavior can be modified via other options. That said many times the current FUSE kernel driver will not always provide the file handle when a client calls `fgetattr`, `fchown`, `fchmod`, `futimens`, `ftruncate`, etc. This means it will call the regular, path based, versions.
|
||||
Some functions, listed in the category `N/A` below, can not be assigned the normal policies. These functions work with file handles, rather than file paths, which were created by `open` or `create`. That said many times the current FUSE kernel driver will not always provide the file handle when a client calls `fgetattr`, `fchown`, `fchmod`, `futimens`, `ftruncate`, etc. This means it will call the regular, path based, versions. `readdir` has no real need for a policy given the purpose is merely to return a list of entries in a directory. `statfs`'s behavior can be modified via other options.
|
||||
|
||||
When using policies which are based on a branch's available space the base path provided is used. Not the full path to the file in question. Meaning that sub mounts won't be considered in the space calculations. The reason is that it doesn't really work for non-path preserving policies and can lead to non-obvious behaviors.
|
||||
When using policies which are based on a branch's available space the base path provided is used. Not the full path to the file in question. Meaning that mounts in the branch won't be considered in the space calculations. The reason is that it doesn't really work for non-path preserving policies and can lead to non-obvious behaviors.
|
||||
|
||||
NOTE: While any policy can be assigned to a function or category though some may not be very useful in practice. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` if there were more than one copy of the file.
|
||||
|
||||
|
||||
#### Functions and their Category classifications
|
||||
### Functions and their Category classifications
|
||||
|
||||
| Category | FUSE Functions |
|
||||
|----------|-------------------------------------------------------------------------------------|
|
||||
| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens |
|
||||
| create | create, mkdir, mknod, symlink |
|
||||
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
|
||||
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
|
||||
| N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write, copy_file_range |
|
||||
|
||||
In cases where something may be searched (to confirm a directory exists across all source mounts) **getattr** will be used.
|
||||
In cases where something may be searched for (such as a path to clone) **getattr** will usually be used.
|
||||
|
||||
|
||||
### Policies
|
||||
|
||||
A policy is the algorithm used to choose a branch or branches for a function to work on. Think of them as ways to filter and sort branches.
|
||||
|
||||
Any function in the `create` category will clone the relative path if needed. Some other functions (`rename`,`link`,`ioctl`) have special requirements or behaviors which you can read more about below.
|
||||
|
||||
|
||||
#### Filtering
|
||||
|
||||
Policies basically search branches and create a list of files / paths for functions to work on. The policy is responsible for filtering and sorting the branches. Filters include **minfreespace**, whether or not a branch is mounted read-only, and the branch tagging (RO,NC,RW). These filters are applied across all policies unless otherwise noted.
|
||||
|
||||
* No **search** function policies filter.
|
||||
* All **action** function policies filter out branches which are mounted **read-only** or tagged as **RO (read-only)**.
|
||||
* All **create** function policies filter out branches which are mounted **read-only**, tagged **RO (read-only)** or **NC (no create)**, or has available space less than `minfreespace`.
|
||||
|
||||
Policies may have their own additional filtering such as those that require existing 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 device) depending on the most recent reason for filtering a branch. **ENOENT** will be returned if no elegible branch is found.
|
||||
|
||||
|
||||
#### Path Preservation
|
||||
@@ -288,20 +383,9 @@ When using non-path preserving policies paths will be cloned to target drives as
|
||||
With the `msp` or `most shared path` policies they are defined as `path preserving` for the purpose of controlling `link` and `rename`'s behaviors since `ignorepponrename` is available to disable that behavior. In mergerfs v3.0 the path preserving behavior of rename and link will likely be separated from the policy all together.
|
||||
|
||||
|
||||
#### Filters
|
||||
|
||||
Policies basically search branches and create a list of files / paths for functions to work on. The policy is responsible for filtering and sorting. Filters include **minfreespace**, whether or not a branch is mounted read-only, and the branch tagging (RO,NC,RW). The policy defines the sorting but filtering is mostly uniform as described below.
|
||||
|
||||
* No **search** policies filter.
|
||||
* All **action** policies will filter out branches which are mounted **read-only** or tagged as **RO (read-only)**.
|
||||
* All **create** policies will filter out branches which are mounted **read-only**, tagged **RO (read-only)** or **NC (no create)**, or has available space less than `minfreespace`.
|
||||
|
||||
If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch.
|
||||
|
||||
|
||||
#### Policy descriptions
|
||||
|
||||
Because of the nature of the behavior the policies act differently depending on the function it is used with (based on the category).
|
||||
A policy's behavior differs, as mentioned above, based on the function it is used with. Sometimes it really might not make sense to even offer certain policies because they are literally the same as others but it makes things a bit more uniform. In mergerfs 3.0 this might change.
|
||||
|
||||
|
||||
| Policy | Description |
|
||||
@@ -344,19 +428,6 @@ Because of the nature of the behavior the policies act differently depending on
|
||||
When `ioctl` is used with an open file then it will use the file handle which was created at the original `open` call. However, when using `ioctl` with a directory mergerfs will use the `open` policy to find the directory to act on.
|
||||
|
||||
|
||||
#### unlink
|
||||
|
||||
In FUSE there is an opaque "file handle" which is created by `open`, `create`, or `opendir`, passed to the kernel, and then is passed back to the FUSE userland application by the kernel. Unfortunately, the FUSE kernel driver does not always send the file handle when it theoretically could/should. This complicates certain behaviors / workflows particularly in the high level API. As a result mergerfs is currently doing a few hacky things.
|
||||
|
||||
libfuse2 and libfuse3, when using the high level API, will rename names to `.fuse_hiddenXXXXXX` if the file is open when unlinked or renamed over. It does this so the file is still available when a request referencing the now missing file is made. This file however keeps a `rmdir` from succeeding and can be picked up by software reading directories.
|
||||
|
||||
The change mergerfs has done is that if a file is open when an unlink or rename happens it will open the file and keep it open till closed by all those who opened it prior. When a request comes in referencing that file and it doesn't include a file handle it will instead use the file handle created at unlink/rename time.
|
||||
|
||||
This won't result in technically proper behavior but close enough for many usecases.
|
||||
|
||||
The plan is to rewrite mergerfs to use the low level API so these invasive libfuse changes are no longer necessary.
|
||||
|
||||
|
||||
#### rename & link ####
|
||||
|
||||
**NOTE:** If you're receiving errors from software when files are moved / renamed / linked then you should consider changing the create policy to one which is **not** path preserving, enabling `ignorepponrename`, or contacting the author of the offending software and requesting that `EXDEV` (cross device / improper link) be properly handled.
|
||||
@@ -410,6 +481,35 @@ The above behavior will help minimize the likelihood of EXDEV being returned but
|
||||
The options `statfs` and `statfs_ignore` can be used to modify `statfs` behavior.
|
||||
|
||||
|
||||
# ERROR HANDLING
|
||||
|
||||
POSIX filesystem functions offer a single return code meaning that there is some complication regarding the handling of multiple branches as mergerfs does. It tries to handle errors in a way that would generally return meaningful values for that particular function.
|
||||
|
||||
### chmod, chown, removexattr, setxattr, truncate, utimens
|
||||
|
||||
1) if no error: return 0 (success)
|
||||
2) if no successes: return first error
|
||||
3) if one of the files acted on was the same as the related search function: return its value
|
||||
4) return 0 (success)
|
||||
|
||||
While doing this increases the complexity and cost of error handling, particularly step 3, this provides probably the most reasonable return value.
|
||||
|
||||
|
||||
### unlink, rmdir
|
||||
|
||||
1) if no errors: return 0 (success)
|
||||
2) return first error
|
||||
|
||||
Older version of mergerfs would return success if any success occurred but for unlink and rmdir there are downstream assumptions that, while not impossible to occur, can confuse some software.
|
||||
|
||||
|
||||
### others
|
||||
|
||||
For search functions there is always a single thing acted on and as such whatever return value that comes from the single function call is returned.
|
||||
|
||||
For create functions `mkdir`, `mknod`, and `symlink` which don't return a file descriptor and therefore can have `all` or `epall` policies it will return success if any of the calls succeed and an error otherwise.
|
||||
|
||||
|
||||
# BUILD / UPDATE
|
||||
|
||||
**NOTE:** Prebuilt packages can be found at and recommended for most users: https://github.com/trapexit/mergerfs/releases
|
||||
@@ -428,7 +528,7 @@ $ wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ve
|
||||
$ cd mergerfs
|
||||
$ sudo tools/install-build-pkgs
|
||||
$ make deb
|
||||
$ sudo dpkg -i ../mergerfs_version_arch.deb
|
||||
$ sudo dpkg -i ../mergerfs_<version>_<arch>.deb
|
||||
```
|
||||
|
||||
#### RHEL / CentOS /Fedora
|
||||
@@ -462,43 +562,26 @@ make LTO=1 - build with link time optimization
|
||||
```
|
||||
|
||||
|
||||
# UPGRADE
|
||||
|
||||
mergerfs can be upgraded live by mounting on top of the previous instance. Simply install the new version of mergerfs and follow the instructions below.
|
||||
|
||||
Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
|
||||
|
||||
```
|
||||
$ sudo mount /mnt/mergerfs
|
||||
$ mount | grep mergerfs
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
```
|
||||
|
||||
A problem with this approach is that the underlying instance will continue to run even if the software using it stop or are restarted. To work around this you can use a "lazy umount". Before mounting over top the mount point with the new instance of mergerfs issue: `umount -l <mergerfs_mountpoint>`.
|
||||
|
||||
|
||||
|
||||
# RUNTIME CONFIG
|
||||
|
||||
#### ioctl
|
||||
|
||||
The original runtime config API was via xattr calls. This however became an issue when needing to disable xattr. While slightly less convenient ioctl does not have the same problems and will be the main API going forward.
|
||||
|
||||
The keys are the same as the command line option arguments as well as the config file.
|
||||
|
||||
##### requests / commands
|
||||
|
||||
All commands take a 4096 byte char buffer.
|
||||
|
||||
* read keys: get a nul '\0' delimited list of option keys
|
||||
* _IOWR(0xDF,0,char[4096]) = 0xD000DF00
|
||||
* on success ioctl return value is the total length
|
||||
* read value: get an option value
|
||||
* _IOWR(0xDF,1,char[4096]) = 0xD000DF01
|
||||
* the key is passed in via the char buffer as a nul '\0' terminated string
|
||||
* on success ioctl return value is the total length
|
||||
* write value: set an option value
|
||||
* _IOW(0xDF,2,char[4096]) = 0x5000DF02
|
||||
* the key and value is passed in via the char buffer as a nul '\0' terminated string in the format of `key=value`
|
||||
* on success ioctl return value is 0
|
||||
* file info: get mergerfs metadata info for a file
|
||||
* _IOWR(0xDF,3,char[4096]) = 0xD000DF03
|
||||
* the key is passed in via the char buffer as a nul '\0' terminated string
|
||||
* on success the ioctl return value is the total length
|
||||
* keys:
|
||||
* basepath: the base mount point for the file according to the getattr policy
|
||||
* relpath: the relative path of the file from the mount point
|
||||
* fullpath: the full path of the underlying file according to the getattr policy
|
||||
* allpaths: a NUL '\0' delimited list of full paths to all files found
|
||||
|
||||
|
||||
#### .mergerfs pseudo file (deprecated) ####
|
||||
|
||||
NOTE: this interface will be removed in mergerfs 3.0
|
||||
#### .mergerfs pseudo file ####
|
||||
|
||||
```
|
||||
<mountpoint>/.mergerfs
|
||||
@@ -511,7 +594,7 @@ Any changes made at runtime are **not** persisted. If you wish for values to per
|
||||
|
||||
##### Keys #####
|
||||
|
||||
Use `xattr -l /mountpoint/.mergerfs` to see all supported keys. Some are informational and therefore read-only. `setxattr` will return EINVAL (invalid argument) on read-only keys.
|
||||
Use `getfattr -d /mountpoint/.mergerfs` or `xattr -l /mountpoint/.mergerfs` to see all supported keys. Some are informational and therefore read-only. `setxattr` will return EINVAL (invalid argument) on read-only keys.
|
||||
|
||||
|
||||
##### Values #####
|
||||
@@ -521,8 +604,6 @@ Same as the command line.
|
||||
|
||||
###### user.mergerfs.branches ######
|
||||
|
||||
**NOTE:** formerly `user.mergerfs.srcmounts` but said key is still supported.
|
||||
|
||||
Used to query or modify the list of branches. When modifying there are several shortcuts to easy manipulation of the list.
|
||||
|
||||
| Value | Description |
|
||||
@@ -542,70 +623,30 @@ The `=NC`, `=RO`, `=RW` syntax works just as on the command line.
|
||||
##### Example #####
|
||||
|
||||
```
|
||||
[trapexit:/mnt/mergerfs] $ xattr -l .mergerfs
|
||||
user.mergerfs.branches: /mnt/a=RW:/mnt/b=RW
|
||||
user.mergerfs.minfreespace: 4294967295
|
||||
user.mergerfs.moveonenospc: false
|
||||
[trapexit:/mnt/mergerfs] $ getfattr -d .mergerfs
|
||||
user.mergerfs.branches="/mnt/a=RW:/mnt/b=RW"
|
||||
user.mergerfs.minfreespace="4294967295"
|
||||
user.mergerfs.moveonenospc="false"
|
||||
...
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
|
||||
ff
|
||||
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
|
||||
user.mergerfs.category.search="ff"
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.category.search newest .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
|
||||
newest
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches +/mnt/c .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
|
||||
/mnt/a:/mnt/b:/mnt/c
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches =/mnt/c .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
|
||||
/mnt/c
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches '+</mnt/a:/mnt/b' .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
|
||||
/mnt/a:/mnt/b:/mnt/c
|
||||
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.category.search -v newest .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
|
||||
user.mergerfs.category.search="newest"
|
||||
```
|
||||
|
||||
|
||||
#### file / directory xattrs ####
|
||||
|
||||
While they won't show up when using [listxattr](http://linux.die.net/man/2/listxattr) **mergerfs** offers a number of special xattrs to query information about the files served. To access the values you will need to issue a [getxattr](http://linux.die.net/man/2/getxattr) for one of the following:
|
||||
While they won't show up when using `getfattr` **mergerfs** offers a number of special xattrs to query information about the files served. To access the values you will need to issue a [getxattr](http://linux.die.net/man/2/getxattr) for one of the following:
|
||||
|
||||
* **user.mergerfs.basepath**: the base mount point for the file given the current getattr policy
|
||||
* **user.mergerfs.relpath**: the relative path of the file from the perspective of the mount point
|
||||
* **user.mergerfs.fullpath**: the full path of the original file given the getattr policy
|
||||
* **user.mergerfs.allpaths**: a NUL ('\0') separated list of full paths to all files found
|
||||
|
||||
```
|
||||
[trapexit:/mnt/mergerfs] $ ls
|
||||
A B C
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.fullpath A
|
||||
/mnt/a/full/path/to/A
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.basepath A
|
||||
/mnt/a
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.relpath A
|
||||
/full/path/to/A
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.allpaths A | tr '\0' '\n'
|
||||
/mnt/a/full/path/to/A
|
||||
/mnt/b/full/path/to/A
|
||||
```
|
||||
|
||||
|
||||
# UPGRADE
|
||||
|
||||
mergerfs can be upgraded live by mounting on top of the previous version. Simply install the new version of mergerfs and follow the instructions below.
|
||||
|
||||
Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
|
||||
|
||||
```
|
||||
$ sudo mount /mnt/mergerfs
|
||||
$ mount | grep mergerfs
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
```
|
||||
|
||||
|
||||
# TOOLING
|
||||
|
||||
@@ -616,7 +657,6 @@ media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,defa
|
||||
* mergerfs.dup: Ensure there are at least N copies of a file across the pool
|
||||
* mergerfs.balance: Rebalance files across drives by moving them from the most filled to the least filled
|
||||
* mergerfs.consolidate: move files within a single mergerfs directory to the drive with most free space
|
||||
* mergerfs.mktrash: Creates FreeDesktop.org Trash specification compatible directories on a mergerfs mount
|
||||
* https://github.com/trapexit/scorch
|
||||
* scorch: A tool to help discover silent corruption of files and keep track of files
|
||||
* https://github.com/trapexit/bbf
|
||||
@@ -716,6 +756,9 @@ With #2 one could use dm-cache as well but there is another solution which requi
|
||||
|
||||
Move files from cache to backing pool based only on the last time the file was accessed. Replace `-atime` with `-amin` if you want minutes rather than days. May want to use the `fadvise` / `--drop-cache` version of rsync or run rsync with the tool "nocache".
|
||||
|
||||
*NOTE:* The arguments to these scripts include the cache **drive**. Not the pool with the cache drive. You could have data loss if the source is the cache pool.
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
@@ -737,6 +780,8 @@ find "${CACHE}" -type f -atime +${N} -printf '%P\n' | \
|
||||
|
||||
Move the oldest file from the cache to the backing pool. Continue till below percentage threshold.
|
||||
|
||||
*NOTE:* The arguments to these scripts include the cache **drive**. Not the pool with the cache drive. You could have data loss if the source is the cache pool.
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
@@ -757,7 +802,7 @@ do
|
||||
head -n 1 | \
|
||||
cut -d' ' -f2-)
|
||||
test -n "${FILE}"
|
||||
rsync -axqHAXWES --preallocate --remove-source-files "${CACHE}/./${FILE}" "${BACKING}/"
|
||||
rsync -axqHAXWESR --preallocate --remove-source-files "${CACHE}/./${FILE}" "${BACKING}/"
|
||||
done
|
||||
```
|
||||
|
||||
@@ -784,6 +829,8 @@ NOTE: be sure to read about these features before changing them to understand wh
|
||||
* use `symlinkify` if your data is largely static and read-only
|
||||
* use tiered cache drives
|
||||
* use LVM and LVM cache to place a SSD in front of your HDDs
|
||||
* if `cache.files` is enabled increase readahead: `echo "1024" > /sys/class/bdi/0:$(stat -c%d /MOUNT)/read_ahead_kb`
|
||||
* increase readahead on all devices: `ls -1 /sys/class/bdi/*/read_ahead_kb | xargs -n1 -I{} sh -c "echo 1024 > {}"`
|
||||
|
||||
If you come across a setting that significantly impacts performance please contact trapexit so he may investigate further.
|
||||
|
||||
@@ -818,15 +865,25 @@ $ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=
|
||||
$ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress
|
||||
```
|
||||
|
||||
### other benchmarks
|
||||
|
||||
If you are attempting to benchmark other behaviors you must ensure you clear kernel caches before runs. In fact it would be a good deal to run before the read and write benchmarks as well just in case.
|
||||
|
||||
```
|
||||
sync
|
||||
echo 3 | sudo tee /proc/sys/vm/drop_caches
|
||||
```
|
||||
|
||||
|
||||
# TIPS / NOTES
|
||||
|
||||
* This document is very literal and thorough. Unless there is a bug things work as described. If a suspected feature isn't mentioned it doesn't exist. If certain libfuse arguments aren't listed they probably shouldn't be used.
|
||||
* Ensure you're using the latest version. Few distros have the latest version.
|
||||
* **use_ino** will only work when used with mergerfs 2.18.0 and above.
|
||||
* Run mergerfs as `root` (with **allow_other**) unless you're merging paths which are owned by the same user otherwise strange permission issues may arise.
|
||||
* https://github.com/trapexit/backup-and-recovery-howtos : A set of guides / howtos on creating a data storage system, backing it up, maintaining it, and recovering from failure.
|
||||
* If you don't see some directories and files you expect in a merged point or policies seem to skip drives be sure the user has permission to all the underlying directories. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
|
||||
* Run mergerfs as `root` (with **allow_other**) unless you're merging paths which are owned exclusively and fully by the same user otherwise strange permission issues may arise. mergerfs is designed and intended to be run as `root`.
|
||||
* If you don't see some directories and files you expect, policies seem to skip branches, you get strange permission errors, etc. be sure the underlying filesystems' permissions are all the same. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
|
||||
* If you still have permission issues be sure you are using POSIX ACL compliant filesystems. mergerfs doesn't generally make exceptions for FAT, NTFS, or other non-POSIX filesystem.
|
||||
* Do **not** use `cache.files=off` if you expect applications (such as rtorrent) to use [mmap](http://linux.die.net/man/2/mmap) files. Shared mmap is not currently supported in FUSE w/ page caching disabled. Enabling `dropcacheonclose` is recommended when `cache.files=partial|full|auto-full`.
|
||||
* Since POSIX functions give only a singular error or success its difficult to determine the proper behavior when applying the function to multiple targets. **mergerfs** will return an error only if all attempts of an action fail. Any success will lead to a success returned. This means however that some odd situations may arise.
|
||||
* [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 determine whether to scan for new content rather than simply performing a full scan. If using the default **getattr** policy of **ff** it's possible those programs will miss an update on account of it returning the first directory found's **stat** info and its a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
|
||||
* Some policies mixed with some functions may result in strange behaviors. Not that some of these behaviors and race conditions couldn't happen outside **mergerfs** but that they are far more likely to occur on account of the attempt to merge together multiple sources of data which could be out of sync due to the different policies.
|
||||
* For consistency its generally best to set **category** wide policies rather than individual **func**'s. This will help limit the confusion of tools such as [rsync](http://linux.die.net/man/1/rsync). However, the flexibility is there if needed.
|
||||
@@ -838,6 +895,7 @@ $ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=
|
||||
|
||||
[https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs](https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs)
|
||||
|
||||
|
||||
#### directory mtime is not being updated
|
||||
|
||||
Remember that the default policy for `getattr` is `ff`. The information for the first directory found will be returned. If it wasn't the directory which had been updated then it will appear outdated.
|
||||
@@ -1014,17 +1072,35 @@ The default create policy is `epmfs`. That is a path preserving algorithm. With
|
||||
This catches a lot of new users off guard but changing the default would break the setup for many existing users. If you do not care about path preservation and wish your files to be spread across all your drives change to `mfs` or similar policy as described above. If you do want path preservation you'll need to perform the manual act of creating paths on the drives you want the data to land on before transferring your data. Setting `func.mkdir=epall` can simplify managing path preservation for `create`. Or use `func.mkdir=rand` if you're interested in just grouping together directory content by drive.
|
||||
|
||||
|
||||
#### Do hard links work?
|
||||
#### Do hardlinks work?
|
||||
|
||||
Yes. You need to use `use_ino` to support proper reporting of inodes but they work regardless. See also the option `inodecalc`.
|
||||
|
||||
What mergerfs does not do is fake hard links across branches. Read the section "rename & link" for how it works.
|
||||
|
||||
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 between them. You should mount in the highest directory in the mergerfs pool that includes all the paths you need if you want links to work.
|
||||
|
||||
#### Does mergerfs support CoW / copy-on-write?
|
||||
|
||||
#### Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
||||
|
||||
Yes. They are completely unreleated pieces of software.
|
||||
|
||||
|
||||
#### Can mergerfs run via Docker, Podman, Kubernetes, etc.
|
||||
|
||||
Yes. With Docker you'll need to include `--cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined` or similar with other container runtimes. You should also be running it as root or given sufficient caps to change user and group identity as well as have root like filesystem permissions.
|
||||
|
||||
Keep in mind that you **MUST** consider identity when using containers. For example: supplemental groups will be picked up from the container unless you properly manage users and groups by sharing relevant /etc files or by using some other means to share identity across containers. Similarly if you use "rootless" containers and user namespaces to do uid/gid translations you **MUST** consider that while managing shared files.
|
||||
|
||||
Also, as mentioned by [hotio](https://hotio.dev/containers/mergerfs), with Docker you should probably be mounting with `bind-propagation` set to `slave`.
|
||||
|
||||
|
||||
#### Does mergerfs support CoW / copy-on-write / writes to read-only filesystems?
|
||||
|
||||
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs or aufs sense. It does offer a [cow-shell](http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html) like hard link breaking (copy to temp file then rename over original) which can be useful when wanting to save space by hardlinking duplicate files but wish to treat each name as if it were a unique and separate file.
|
||||
|
||||
If you want to write to a read-only filesystem you should look at overlayfs. You can always include the overlayfs mount into a mergerfs pool.
|
||||
|
||||
|
||||
#### Why can't I see my files / directories?
|
||||
|
||||
@@ -1052,7 +1128,7 @@ If `mergerfs` doesn't work as a type it could be due to how the `mount.mergerfs`
|
||||
|
||||
See above first.
|
||||
|
||||
If/when mergerfs is rewritten to use the low-level API then it'll be plausible to support system libfuse but till then its simply too much work to manage the differences across the versions.
|
||||
If/when mergerfs is rewritten to use the low-level API then it'll be plausible to support system libfuse but till then it's simply too much work to manage the differences across the versions.
|
||||
|
||||
|
||||
#### Why use mergerfs over mhddfs?
|
||||
@@ -1193,49 +1269,61 @@ Filesystems are complex and difficult to debug. mergerfs, while being just a pro
|
||||
#### Information to include in bug reports
|
||||
|
||||
* Version of mergerfs: `mergerfs -V`
|
||||
* mergerfs settings: from `/etc/fstab` or command line execution
|
||||
* Version of Linux: `uname -a`
|
||||
* Versions of any additional software being used
|
||||
* List of drives, their filesystems, and sizes (before and after issue): `df -h`
|
||||
* **All** information about the relevant branches and paths: permissions, etc.
|
||||
* mergerfs settings / arguments: from fstab, systemd unit, command line, etc.
|
||||
* Version of the OS: `uname -a`
|
||||
* List of branches, their filesystem types, sizes (before and after issue): `df -h`
|
||||
* **All** information about the relevant branches and paths: permissions, ownership, etc.
|
||||
* **All** information about the client app making the requests: version, uid/gid
|
||||
* Runtime environment: mostly are things running inside containers or not
|
||||
* A `strace` of the app having problems:
|
||||
* `strace -f -o /tmp/app.strace.txt <cmd>`
|
||||
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
|
||||
* A `strace` of mergerfs while the program is trying to do whatever it's failing to do:
|
||||
* `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
* **Precise** directions on replicating the issue. Do not leave **anything** out.
|
||||
* Try to recreate the problem in the simplest way using standard programs.
|
||||
* Try to recreate the problem in the simplest way using standard programs: ln, mv, cp, ls, dd, etc.
|
||||
|
||||
|
||||
#### Contact / Issue submission
|
||||
|
||||
* github.com: https://github.com/trapexit/mergerfs/issues
|
||||
* email: trapexit@spawn.link
|
||||
* discord: https://discord.gg/MpAr69V
|
||||
* twitter: https://twitter.com/_trapexit
|
||||
* reddit: https://www.reddit.com/user/trapexit
|
||||
* discord: https://discord.gg/MpAr69V
|
||||
|
||||
|
||||
#### Support development
|
||||
|
||||
This software is free to use and released under a very liberal license (ISC). That said if you like this software and would like to support its development donations are welcome.
|
||||
|
||||
At the moment my preference would be GitHub Sponsors only because I am part of the matching program. That said please use whatever platform you prefer.
|
||||
Crypto is fine in whatever protocol you prefer. My preferences for fiat would be GitHub Sponsors or PayPal though feel free to use any platform listed below.
|
||||
|
||||
* PayPal: https://paypal.me/trapexit
|
||||
* GitHub Sponsors: https://github.com/sponsors/trapexit
|
||||
* PayPal: https://paypal.me/trapexit
|
||||
* Patreon: https://www.patreon.com/trapexit
|
||||
* SubscribeStar: https://www.subscribestar.com/trapexit
|
||||
* BuyMeACoffee: https://buymeacoff.ee/trapexit
|
||||
* Ko-Fi: https://ko-fi.com/trapexit
|
||||
* Open Collective: https://opencollective.com/trapexit
|
||||
* Bitcoin (BTC): 1DfoUd2m5WCxJAMvcFuvDpT4DR2gWX2PWb
|
||||
* Bitcoin Cash (BCH): qrf257j0l09yxty4kur8dk2uma8p5vntdcpks72l8z
|
||||
* Ethereum (ETH): 0xb486C0270fF75872Fc51d85879b9c15C380E66CA
|
||||
* Litecoin (LTC): LW1rvHRPWtm2NUEMhJpP4DjHZY1FaJ1WYs
|
||||
* Monero (XMR): 8AuU7PeK1fVhGP9yug8fdgKBssvUQoBVFKGhtT5DzWQt7fcTKC1SUx3Eb7xCAiVt3McWJp2Z9gX2wU7SPhh1GfWYBTCs6SS
|
||||
* Basic Attention Token (BAT): 0xE651d4900B4C305284Da43E2e182e9abE149A87A
|
||||
* Bitcoin (BTC): bc1qu537hqlnmn2wawx9n7nws0dlkz55h0cd93ny28
|
||||
* Bitcoin Cash (BCH): bitcoincash:qqp0vh9v44us74gaggwjfv9y54zfjmmd7srlqxa3xt
|
||||
* Bitcoin SV (BSV): 1FkFuxRtt3f8LbkpeUKRZq7gKJFzGSGgZV
|
||||
* Bitcoin Gold (BTG): AaPuJgJeohPjkB3LxJM6NKGnaHoRJ8ieT3
|
||||
* Litecoin (LTC): MJQzsHBdNnkyGqCFdcAdHYKugicBmfAXfQ
|
||||
* Dogecoin (DOGE): DLJNLVe28vZ4SMQSxDJLBQBv57rGtUoWFh
|
||||
* Ethereum (ETH): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
||||
* Any ERC20 Token: 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
||||
* Ethereum Classic (ETC): 0x2B6054428e69a1201B6555f7a2aEc0Fba01EAD9F
|
||||
* Harmony (ONE): one1hrtd2hqrrx4vcvncvrgnlzg5yl9wahn66lq6rw (0xB8d6d55c0319aacC327860d13f891427caEede7a)
|
||||
* Monero (XMR): 45BBZMrJwPSaFwSoqLVNEggWR2BJJsXxz7bNz8FXnnFo3GyhVJFSCrCFSS7zYwDa9r1TmFmGMxQ2HTntuc11yZ9q1LeCE8f
|
||||
* Dash (DASH): XvsFrohu8tbjA4E8p7xsc86E2ADxLHGXHL
|
||||
* Chia (XCH): xch18l7e2q34jtuyzkq0jg8vp7yrtnfwzly30w3yyhkk96um2ur4yqcq6p2een
|
||||
* LBRY Credits (LBC): bFusyoZPkSuzM2Pr8mcthgvkymaosJZt5r
|
||||
* Zcash (ZEC): t1ZwTgmbQF23DJrzqbAmw8kXWvU2xUkkhTt
|
||||
* Zcoin (XZC): a8L5Vz35KdCQe7Y7urK2pcCGau7JsqZ5Gw
|
||||
* Ripple (XRP): r9f6aoxaGD8aymxqH89Ke1PCUPkNiFdZZC
|
||||
* Tezos (XTZ): tz1ZxerkbbALsuU9XGV9K9fFpuLWnKAGfc1C
|
||||
* Zcash (ZEC): t1Zo1GGn2T3GrhKvgdtnTsTnWu6tCPaCaHG
|
||||
* DigiByte (DGB): Sb8r1qTrryY9Sp4YkTE1eeKEGVzgArnE5N
|
||||
* Namecoin (NMC): NDzb9FkoptGu5QbgetCkodJqo2zE1cTwyb
|
||||
* Vertcoin (VTC): 3PYdhokAGXJwWrwHRoTywxG4iUDk6EHjKe
|
||||
* Other crypto currencies: contact me for address
|
||||
|
||||
|
||||
|
||||
+7
-3
@@ -10,7 +10,7 @@ INSTALLUTILS :=
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
@@ -33,12 +33,15 @@ AR ?= ar
|
||||
|
||||
SRC = \
|
||||
lib/buffer.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/crc32b.c \
|
||||
lib/debug.c \
|
||||
lib/fuse.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/fuse_kern_chan.c \
|
||||
lib/fuse_loop_mt.c \
|
||||
lib/fuse_lowlevel.c \
|
||||
lib/fuse_mt.c \
|
||||
lib/fuse_node.c \
|
||||
lib/fuse_opt.c \
|
||||
lib/fuse_session.c \
|
||||
lib/fuse_signals.c \
|
||||
@@ -49,7 +52,8 @@ DEPS = $(SRC:lib/%.c=build/%.d)
|
||||
CFLAGS ?= \
|
||||
$(OPT_FLAGS)
|
||||
CFLAGS := \
|
||||
${CFLAGS} \
|
||||
${CFLAGS} \
|
||||
-std=gnu99 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
-MMD
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <malloc.h>
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
malloc_trim(0);
|
||||
}
|
||||
+94
-135
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "extern_c.h"
|
||||
#include "fuse_common.h"
|
||||
#include "fuse_dirents.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
@@ -47,6 +46,9 @@ struct fuse;
|
||||
/** Structure containing a raw command */
|
||||
struct fuse_cmd;
|
||||
|
||||
struct fuse_dirents_t;
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
|
||||
/**
|
||||
* The file system operations:
|
||||
*
|
||||
@@ -121,31 +123,25 @@ struct fuse_operations
|
||||
int (*rmdir) (const char *);
|
||||
|
||||
/** Create a symbolic link */
|
||||
int (*symlink) (const char *, const char *);
|
||||
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 *);
|
||||
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 struct fuse_file_info *, const mode_t);
|
||||
int (*fchmod)(const fuse_file_info_t *, const mode_t);
|
||||
|
||||
/** Change the owner and group of a file */
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*fchown)(const struct fuse_file_info *, const uid_t, const gid_t);
|
||||
int (*fchown)(const fuse_file_info_t *, const uid_t, const gid_t);
|
||||
|
||||
/** Change the size of a file */
|
||||
int (*truncate) (const char *, off_t);
|
||||
|
||||
/** Change the access and/or modification times of a file
|
||||
*
|
||||
* Deprecated, use utimens() instead.
|
||||
*/
|
||||
int (*utime) (const char *, struct utimbuf *);
|
||||
|
||||
/** File open operation
|
||||
*
|
||||
* No creation (O_CREAT, O_EXCL) and by default also no
|
||||
@@ -163,32 +159,7 @@ struct fuse_operations
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*open) (const char *, struct fuse_file_info *);
|
||||
|
||||
/** Read data from an open file
|
||||
*
|
||||
* Read should return exactly the number of bytes requested except
|
||||
* on EOF or error, otherwise the rest of the data will be
|
||||
* substituted with zeroes. An exception to this is when the
|
||||
* 'direct_io' mount option is specified, in which case the return
|
||||
* value of the read system call will reflect the return value of
|
||||
* this operation.
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*read) (char *, size_t, off_t,
|
||||
struct fuse_file_info *);
|
||||
|
||||
/** Write data to an open file
|
||||
*
|
||||
* Write should return exactly the number of bytes requested
|
||||
* except on error. An exception to this is when the 'direct_io'
|
||||
* mount option is specified (see read operation).
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*write) (const char *, size_t, off_t,
|
||||
struct fuse_file_info *);
|
||||
int (*open) (const char *, fuse_file_info_t *);
|
||||
|
||||
/** Get file system statistics
|
||||
*
|
||||
@@ -222,7 +193,7 @@ struct fuse_operations
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*flush) (struct fuse_file_info *);
|
||||
int (*flush) (const fuse_file_info_t *);
|
||||
|
||||
/** Release an open file
|
||||
*
|
||||
@@ -238,7 +209,7 @@ struct fuse_operations
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*release) (struct fuse_file_info *);
|
||||
int (*release) (const fuse_file_info_t *);
|
||||
|
||||
/** Synchronize file contents
|
||||
*
|
||||
@@ -247,7 +218,7 @@ struct fuse_operations
|
||||
*
|
||||
* Changed in version 2.2
|
||||
*/
|
||||
int (*fsync) (int, struct fuse_file_info *);
|
||||
int (*fsync) (const fuse_file_info_t *, int);
|
||||
|
||||
/** Set extended attributes */
|
||||
int (*setxattr) (const char *, const char *, const char *, size_t, int);
|
||||
@@ -271,7 +242,8 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*opendir) (const char *, struct fuse_file_info *);
|
||||
int (*opendir) (const char *,
|
||||
fuse_file_info_t *);
|
||||
|
||||
/** Read directory
|
||||
*
|
||||
@@ -294,10 +266,10 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*readdir)(struct fuse_file_info *,
|
||||
int (*readdir)(const fuse_file_info_t *,
|
||||
fuse_dirents_t *);
|
||||
|
||||
int (*readdir_plus)(struct fuse_file_info *,
|
||||
int (*readdir_plus)(const fuse_file_info_t *,
|
||||
fuse_dirents_t *);
|
||||
|
||||
|
||||
@@ -305,7 +277,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*releasedir) (struct fuse_file_info *);
|
||||
int (*releasedir) (const fuse_file_info_t *);
|
||||
|
||||
/** Synchronize directory contents
|
||||
*
|
||||
@@ -314,7 +286,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
int (*fsyncdir) (int, struct fuse_file_info *);
|
||||
int (*fsyncdir) (const fuse_file_info_t *, int);
|
||||
|
||||
/**
|
||||
* Initialize filesystem
|
||||
@@ -335,7 +307,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
*/
|
||||
void (*destroy) (void *);
|
||||
void (*destroy) (void);
|
||||
|
||||
/**
|
||||
* Check file access permissions
|
||||
@@ -362,7 +334,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*create) (const char *, mode_t, struct fuse_file_info *);
|
||||
int (*create) (const char *, mode_t, fuse_file_info_t *);
|
||||
|
||||
/**
|
||||
* Change the size of an open file
|
||||
@@ -376,7 +348,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*ftruncate) (off_t, struct fuse_file_info *);
|
||||
int (*ftruncate) (const fuse_file_info_t *, off_t);
|
||||
|
||||
/**
|
||||
* Get attributes from an open file
|
||||
@@ -390,7 +362,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.5
|
||||
*/
|
||||
int (*fgetattr) (struct stat *, struct fuse_file_info *, fuse_timeouts_t *);
|
||||
int (*fgetattr) (const fuse_file_info_t *, struct stat *, fuse_timeouts_t *);
|
||||
|
||||
/**
|
||||
* Perform POSIX file locking operation
|
||||
@@ -424,7 +396,8 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*lock) (struct fuse_file_info *, int cmd,
|
||||
int (*lock) (const fuse_file_info_t *,
|
||||
int cmd,
|
||||
struct flock *);
|
||||
|
||||
/**
|
||||
@@ -439,7 +412,7 @@ struct fuse_operations
|
||||
* Introduced in version 2.6
|
||||
*/
|
||||
int (*utimens)(const char *, const struct timespec tv[2]);
|
||||
int (*futimens)(const struct fuse_file_info *ffi_, const struct timespec tv_[2]);
|
||||
int (*futimens)(const fuse_file_info_t *ffi_, const struct timespec tv_[2]);
|
||||
|
||||
/**
|
||||
* Map block index within file to block index within device
|
||||
@@ -466,12 +439,12 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*ioctl) (unsigned long cmd,
|
||||
void *arg,
|
||||
struct fuse_file_info *ffi,
|
||||
unsigned int flags,
|
||||
void *data,
|
||||
uint32_t *out_bufsz);
|
||||
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
|
||||
@@ -490,8 +463,9 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*poll) (struct fuse_file_info *,
|
||||
struct fuse_pollhandle *ph, unsigned *reventsp);
|
||||
int (*poll) (const fuse_file_info_t *ffi,
|
||||
fuse_pollhandle_t *ph,
|
||||
unsigned *reventsp);
|
||||
|
||||
/** Write contents of buffer to an open file
|
||||
*
|
||||
@@ -501,8 +475,9 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*write_buf) (struct fuse_bufvec *buf, off_t off,
|
||||
struct fuse_file_info *);
|
||||
int (*write_buf) (const fuse_file_info_t *ffi,
|
||||
struct fuse_bufvec *buf,
|
||||
off_t off);
|
||||
|
||||
/** Store data from an open file in a buffer
|
||||
*
|
||||
@@ -520,8 +495,10 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*read_buf) (struct fuse_bufvec **bufp,
|
||||
size_t size, off_t off, struct fuse_file_info *);
|
||||
int (*read_buf) (const fuse_file_info_t *ffi,
|
||||
struct fuse_bufvec **bufp,
|
||||
size_t size,
|
||||
off_t off);
|
||||
/**
|
||||
* Perform BSD file locking operation
|
||||
*
|
||||
@@ -542,7 +519,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.9
|
||||
*/
|
||||
int (*flock) (struct fuse_file_info *, int op);
|
||||
int (*flock) (const fuse_file_info_t *, int op);
|
||||
|
||||
/**
|
||||
* Allocates space for an open file
|
||||
@@ -554,7 +531,7 @@ struct fuse_operations
|
||||
*
|
||||
* Introduced in version 2.9.1
|
||||
*/
|
||||
int (*fallocate) (int, off_t, off_t,struct fuse_file_info *);
|
||||
int (*fallocate) (const fuse_file_info_t *, int, off_t, off_t);
|
||||
|
||||
/**
|
||||
* Copy a range of data from one file to another
|
||||
@@ -569,12 +546,12 @@ struct fuse_operations
|
||||
* destination. Effectively doing an inefficient copy of the
|
||||
* data.
|
||||
*/
|
||||
ssize_t (*copy_file_range)(struct fuse_file_info *fi_in,
|
||||
off_t offset_in,
|
||||
struct fuse_file_info *fi_out,
|
||||
off_t offset_out,
|
||||
size_t size,
|
||||
int flags);
|
||||
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);
|
||||
};
|
||||
|
||||
/** Extra context that may be needed by some filesystems
|
||||
@@ -596,9 +573,6 @@ struct fuse_context
|
||||
/** Thread ID of the calling process */
|
||||
pid_t pid;
|
||||
|
||||
/** Private filesystem data */
|
||||
void *private_data;
|
||||
|
||||
/** Umask of the calling process (introduced in version 2.8) */
|
||||
mode_t umask;
|
||||
};
|
||||
@@ -623,15 +597,13 @@ struct fuse_context
|
||||
* @param argc the argument counter passed to the main() function
|
||||
* @param argv the argument vector passed to the main() function
|
||||
* @param op the file system operation
|
||||
* @param user_data user data supplied in the context during the init() method
|
||||
* @return 0 on success, nonzero on failure
|
||||
*/
|
||||
/*
|
||||
int fuse_main(int argc, char *argv[], const struct fuse_operations *op,
|
||||
void *user_data);
|
||||
int fuse_main(int argc, char *argv[], const struct fuse_operations *op);
|
||||
*/
|
||||
#define fuse_main(argc, argv, op, user_data) \
|
||||
fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)
|
||||
#define fuse_main(argc, argv, op) \
|
||||
fuse_main_real(argc, argv, op, sizeof(*(op)))
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* More detailed API *
|
||||
@@ -644,12 +616,10 @@ struct fuse_context
|
||||
* @param args argument vector
|
||||
* @param op the filesystem operations
|
||||
* @param op_size the size of the fuse_operations structure
|
||||
* @param user_data user data supplied in the context during the init() method
|
||||
* @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,
|
||||
void *user_data);
|
||||
const struct fuse_operations *op, size_t op_size);
|
||||
|
||||
/**
|
||||
* Destroy the FUSE handle.
|
||||
@@ -719,25 +689,14 @@ 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, void *user_data);
|
||||
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, size_t op_size);
|
||||
|
||||
/**
|
||||
* Start the cleanup thread when using option "remember".
|
||||
*
|
||||
* This is done automatically by fuse_loop_mt()
|
||||
* @param fuse struct fuse pointer for fuse instance
|
||||
* @return 0 on success and -1 on error
|
||||
*/
|
||||
int fuse_start_cleanup_thread(struct fuse *fuse);
|
||||
int fuse_start_maintenance_thread(struct fuse *fuse);
|
||||
void fuse_stop_maintenance_thread(struct fuse *fuse);
|
||||
|
||||
int fuse_log_metrics_get(void);
|
||||
void fuse_log_metrics_set(int enabled);
|
||||
|
||||
/**
|
||||
* Stop the cleanup thread when using option "remember".
|
||||
*
|
||||
* This is done automatically by fuse_loop_mt()
|
||||
* @param fuse struct fuse pointer for fuse instance
|
||||
*/
|
||||
void fuse_stop_cleanup_thread(struct fuse *fuse);
|
||||
|
||||
/**
|
||||
* Iterate over cache removing stale entries
|
||||
@@ -775,57 +734,60 @@ int fuse_fs_getattr(struct fuse_fs *fs,
|
||||
struct stat *buf,
|
||||
fuse_timeouts_t *timeout);
|
||||
|
||||
int fuse_fs_fgetattr(struct fuse_fs *fs,
|
||||
struct stat *buf,
|
||||
struct fuse_file_info *fi,
|
||||
fuse_timeouts_t *timeout);
|
||||
int fuse_fs_fgetattr(struct fuse_fs *fs,
|
||||
struct stat *buf,
|
||||
fuse_file_info_t *fi,
|
||||
fuse_timeouts_t *timeout);
|
||||
|
||||
int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
|
||||
const char *newpath);
|
||||
int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
|
||||
int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
|
||||
int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
|
||||
const char *path);
|
||||
int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath);
|
||||
int fuse_fs_symlink(struct fuse_fs *fs,
|
||||
const char *linkname,
|
||||
const char *path,
|
||||
struct stat *st,
|
||||
fuse_timeouts_t *timeouts);
|
||||
int fuse_fs_link(struct fuse_fs *fs,
|
||||
const char *oldpath,
|
||||
const char *newpath,
|
||||
struct stat *st,
|
||||
fuse_timeouts_t *timeouts);
|
||||
int fuse_fs_release(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_open(struct fuse_fs *fs, const char *path,
|
||||
struct fuse_file_info *fi);
|
||||
int fuse_fs_read(struct fuse_fs *fs, char *buf, size_t size,
|
||||
off_t off, struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_read_buf(struct fuse_fs *fs,
|
||||
struct fuse_bufvec **bufp, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
int fuse_fs_write(struct fuse_fs *fs, const char *buf,
|
||||
size_t size, off_t off, struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_write_buf(struct fuse_fs *fs,
|
||||
struct fuse_bufvec *buf, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_fsync(struct fuse_fs *fs, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_flush(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf);
|
||||
int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_readdir(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi,
|
||||
fuse_file_info_t *fi,
|
||||
fuse_dirents_t *buf);
|
||||
int fuse_fs_fsyncdir(struct fuse_fs *fs, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_releasedir(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_lock(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi, int cmd, struct flock *lock);
|
||||
fuse_file_info_t *fi, int cmd, struct flock *lock);
|
||||
int fuse_fs_flock(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi, int op);
|
||||
fuse_file_info_t *fi, int op);
|
||||
int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode);
|
||||
int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid);
|
||||
int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size);
|
||||
int fuse_fs_ftruncate(struct fuse_fs *fs, off_t size,
|
||||
struct fuse_file_info *fi);
|
||||
fuse_file_info_t *fi);
|
||||
int fuse_fs_utimens(struct fuse_fs *fs, const char *path,
|
||||
const struct timespec tv[2]);
|
||||
int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask);
|
||||
@@ -845,24 +807,24 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
|
||||
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
|
||||
uint64_t *idx);
|
||||
int fuse_fs_ioctl(struct fuse_fs *fs, unsigned long cmd, void *arg,
|
||||
struct fuse_file_info *fi, unsigned int flags,
|
||||
fuse_file_info_t *fi, unsigned int flags,
|
||||
void *data, uint32_t *out_bufsz);
|
||||
int fuse_fs_poll(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi, struct fuse_pollhandle *ph,
|
||||
fuse_file_info_t *fi, fuse_pollhandle_t *ph,
|
||||
unsigned *reventsp);
|
||||
int fuse_fs_fallocate(struct fuse_fs *fs, int mode,
|
||||
off_t offset, off_t length, struct fuse_file_info *fi);
|
||||
off_t offset, off_t length, fuse_file_info_t *fi);
|
||||
void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
|
||||
void fuse_fs_destroy(struct fuse_fs *fs);
|
||||
|
||||
int fuse_fs_prepare_hide(struct fuse_fs *fs, const char *path, uint64_t *fh);
|
||||
int fuse_fs_free_hide(struct fuse_fs *fs, uint64_t fh);
|
||||
ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs,
|
||||
struct fuse_file_info *fi_in, off_t off_in,
|
||||
struct fuse_file_info *fi_out, off_t off_out,
|
||||
fuse_file_info_t *fi_in, off_t off_in,
|
||||
fuse_file_info_t *fi_out, off_t off_out,
|
||||
size_t len, int flags);
|
||||
|
||||
int fuse_notify_poll(struct fuse_pollhandle *ph);
|
||||
int fuse_notify_poll(fuse_pollhandle_t *ph);
|
||||
|
||||
/**
|
||||
* Create a new fuse filesystem object
|
||||
@@ -872,11 +834,9 @@ int fuse_notify_poll(struct fuse_pollhandle *ph);
|
||||
*
|
||||
* @param op the filesystem operations
|
||||
* @param op_size the size of the fuse_operations structure
|
||||
* @param user_data user data supplied in the context during the init() method
|
||||
* @return a new filesystem object
|
||||
*/
|
||||
struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size,
|
||||
void *user_data);
|
||||
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... *
|
||||
@@ -891,8 +851,7 @@ 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,
|
||||
char **mountpoint,
|
||||
void *user_data);
|
||||
char **mountpoint);
|
||||
|
||||
/** This is the part of fuse_main() after the event loop */
|
||||
void fuse_teardown(struct fuse *fuse, char *mountpoint);
|
||||
|
||||
@@ -46,8 +46,8 @@ EXTERN_C_BEGIN
|
||||
*
|
||||
* Changed in version 2.5
|
||||
*/
|
||||
struct
|
||||
fuse_file_info
|
||||
typedef struct fuse_file_info_t fuse_file_info_t;
|
||||
struct fuse_file_info_t
|
||||
{
|
||||
/** Open flags. Available in open() and release() */
|
||||
int flags;
|
||||
@@ -205,7 +205,8 @@ struct fuse_conn_info {
|
||||
|
||||
struct fuse_session;
|
||||
struct fuse_chan;
|
||||
struct fuse_pollhandle;
|
||||
struct fuse_pollhandle_t;
|
||||
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
|
||||
|
||||
/**
|
||||
* Create a FUSE mountpoint
|
||||
@@ -271,7 +272,7 @@ int fuse_version(void);
|
||||
*
|
||||
* @param ph the poll handle
|
||||
*/
|
||||
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
|
||||
void fuse_pollhandle_destroy(fuse_pollhandle_t *ph);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Data buffer *
|
||||
|
||||
@@ -43,14 +43,12 @@ enum fuse_dirents_type_e
|
||||
};
|
||||
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
|
||||
|
||||
typedef struct fuse_dirents_s fuse_dirents_t;
|
||||
struct fuse_dirents_s
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
struct fuse_dirents_t
|
||||
{
|
||||
char *buf;
|
||||
uint64_t buf_len;
|
||||
uint64_t data_len;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
kvec_t(char) data;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
};
|
||||
|
||||
int fuse_dirents_init(fuse_dirents_t *d);
|
||||
|
||||
+206
-237
@@ -43,9 +43,6 @@ EXTERN_C_BEGIN
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
|
||||
/** Inode number type */
|
||||
typedef uint64_t fuse_ino_t;
|
||||
|
||||
/** Request pointer type */
|
||||
typedef struct fuse_req *fuse_req_t;
|
||||
|
||||
@@ -67,63 +64,63 @@ struct fuse_chan;
|
||||
/** Directory entry parameters supplied to fuse_reply_entry() */
|
||||
struct fuse_entry_param
|
||||
{
|
||||
/** Unique inode number
|
||||
*
|
||||
* In lookup, zero means negative entry (from version 2.5)
|
||||
* Returning ENOENT also means negative entry, but by setting zero
|
||||
* ino the kernel may cache negative entries for entry_timeout
|
||||
* seconds.
|
||||
*/
|
||||
fuse_ino_t ino;
|
||||
/** Unique inode number
|
||||
*
|
||||
* In lookup, zero means negative entry (from version 2.5)
|
||||
* Returning ENOENT also means negative entry, but by setting zero
|
||||
* ino the kernel may cache negative entries for entry_timeout
|
||||
* seconds.
|
||||
*/
|
||||
uint64_t ino;
|
||||
|
||||
/** Generation number for this entry.
|
||||
*
|
||||
* If the file system will be exported over NFS, the
|
||||
* ino/generation pairs need to be unique over the file
|
||||
* system's lifetime (rather than just the mount time). So if
|
||||
* the file system reuses an inode after it has been deleted,
|
||||
* it must assign a new, previously unused generation number
|
||||
* to the inode at the same time.
|
||||
*
|
||||
* The generation must be non-zero, otherwise FUSE will treat
|
||||
* it as an error.
|
||||
*
|
||||
*/
|
||||
uint64_t generation;
|
||||
/** Generation number for this entry.
|
||||
*
|
||||
* If the file system will be exported over NFS, the
|
||||
* ino/generation pairs need to be unique over the file
|
||||
* system's lifetime (rather than just the mount time). So if
|
||||
* the file system reuses an inode after it has been deleted,
|
||||
* it must assign a new, previously unused generation number
|
||||
* to the inode at the same time.
|
||||
*
|
||||
* The generation must be non-zero, otherwise FUSE will treat
|
||||
* it as an error.
|
||||
*
|
||||
*/
|
||||
uint64_t generation;
|
||||
|
||||
|
||||
/** Inode attributes.
|
||||
*
|
||||
* Even if attr_timeout == 0, attr must be correct. For example,
|
||||
* for open(), FUSE uses attr.st_size from lookup() to determine
|
||||
* how many bytes to request. If this value is not correct,
|
||||
* incorrect data will be returned.
|
||||
*/
|
||||
struct stat attr;
|
||||
/** Inode attributes.
|
||||
*
|
||||
* Even if attr_timeout == 0, attr must be correct. For example,
|
||||
* for open(), FUSE uses attr.st_size from lookup() to determine
|
||||
* how many bytes to request. If this value is not correct,
|
||||
* incorrect data will be returned.
|
||||
*/
|
||||
struct stat attr;
|
||||
|
||||
fuse_timeouts_t timeout;
|
||||
fuse_timeouts_t timeout;
|
||||
};
|
||||
|
||||
/** Additional context associated with requests */
|
||||
struct fuse_ctx
|
||||
{
|
||||
/** User ID of the calling process */
|
||||
uid_t uid;
|
||||
/** User ID of the calling process */
|
||||
uid_t uid;
|
||||
|
||||
/** Group ID of the calling process */
|
||||
gid_t gid;
|
||||
/** Group ID of the calling process */
|
||||
gid_t gid;
|
||||
|
||||
/** Thread ID of the calling process */
|
||||
pid_t pid;
|
||||
/** Thread ID of the calling process */
|
||||
pid_t pid;
|
||||
|
||||
/** Umask of the calling process (introduced in version 2.8) */
|
||||
mode_t umask;
|
||||
/** Umask of the calling process (introduced in version 2.8) */
|
||||
mode_t umask;
|
||||
};
|
||||
|
||||
struct fuse_forget_data
|
||||
{
|
||||
fuse_ino_t ino;
|
||||
uint64_t nlookup;
|
||||
uint64_t ino;
|
||||
uint64_t nlookup;
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
@@ -153,78 +150,78 @@ uint64_t nlookup;
|
||||
*/
|
||||
struct fuse_lowlevel_ops
|
||||
{
|
||||
/**
|
||||
* Initialize filesystem
|
||||
*
|
||||
* Called before any other filesystem method
|
||||
*
|
||||
* There's no reply to this function
|
||||
*
|
||||
* @param userdata the user data passed to fuse_lowlevel_new()
|
||||
*/
|
||||
void (*init) (void *userdata, struct fuse_conn_info *conn);
|
||||
/**
|
||||
* Initialize filesystem
|
||||
*
|
||||
* Called before any other filesystem method
|
||||
*
|
||||
* There's no reply to this function
|
||||
*
|
||||
* @param userdata the user data passed to fuse_lowlevel_new()
|
||||
*/
|
||||
void (*init)(void *userdata, struct fuse_conn_info *conn);
|
||||
|
||||
/**
|
||||
* Clean up filesystem
|
||||
*
|
||||
* Called on filesystem exit
|
||||
*
|
||||
* There's no reply to this function
|
||||
*
|
||||
* @param userdata the user data passed to fuse_lowlevel_new()
|
||||
*/
|
||||
void (*destroy) (void *userdata);
|
||||
/**
|
||||
* Clean up filesystem
|
||||
*
|
||||
* Called on filesystem exit
|
||||
*
|
||||
* There's no reply to this function
|
||||
*
|
||||
* @param userdata the user data passed to fuse_lowlevel_new()
|
||||
*/
|
||||
void (*destroy)(void *userdata);
|
||||
|
||||
/**
|
||||
* Look up a directory entry by name and get its attributes.
|
||||
*
|
||||
* Valid replies:
|
||||
* fuse_reply_entry
|
||||
* fuse_reply_err
|
||||
*
|
||||
* @param req request handle
|
||||
* @param parent inode number of the parent directory
|
||||
* @param name the name to look up
|
||||
*/
|
||||
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
/**
|
||||
* Look up a directory entry by name and get its attributes.
|
||||
*
|
||||
* Valid replies:
|
||||
* fuse_reply_entry
|
||||
* fuse_reply_err
|
||||
*
|
||||
* @param req request handle
|
||||
* @param parent inode number of the parent directory
|
||||
* @param name the name to look up
|
||||
*/
|
||||
void (*lookup)(fuse_req_t req, uint64_t parent, const char *name);
|
||||
|
||||
/**
|
||||
* Forget about an inode
|
||||
*
|
||||
* This function is called when the kernel removes an inode
|
||||
* from its internal caches.
|
||||
*
|
||||
* The inode's lookup count increases by one for every call to
|
||||
* fuse_reply_entry and fuse_reply_create. The nlookup parameter
|
||||
* indicates by how much the lookup count should be decreased.
|
||||
*
|
||||
* Inodes with a non-zero lookup count may receive request from
|
||||
* the kernel even after calls to unlink, rmdir or (when
|
||||
* overwriting an existing file) rename. Filesystems must handle
|
||||
* such requests properly and it is recommended to defer removal
|
||||
* of the inode until the lookup count reaches zero. Calls to
|
||||
* unlink, remdir or rename will be followed closely by forget
|
||||
* unless the file or directory is open, in which case the
|
||||
* kernel issues forget only after the release or releasedir
|
||||
* calls.
|
||||
*
|
||||
* Note that if a file system will be exported over NFS the
|
||||
* inodes lifetime must extend even beyond forget. See the
|
||||
* generation field in struct fuse_entry_param above.
|
||||
*
|
||||
* On unmount the lookup count for all inodes implicitly drops
|
||||
* to zero. It is not guaranteed that the file system will
|
||||
* receive corresponding forget messages for the affected
|
||||
* inodes.
|
||||
*
|
||||
* Valid replies:
|
||||
* fuse_reply_none
|
||||
*
|
||||
* @param req request handle
|
||||
* @param ino the inode number
|
||||
* @param nlookup the number of lookups to forget
|
||||
*/
|
||||
void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup);
|
||||
/**
|
||||
* Forget about an inode
|
||||
*
|
||||
* This function is called when the kernel removes an inode
|
||||
* from its internal caches.
|
||||
*
|
||||
* The inode's lookup count increases by one for every call to
|
||||
* fuse_reply_entry and fuse_reply_create. The nlookup parameter
|
||||
* indicates by how much the lookup count should be decreased.
|
||||
*
|
||||
* Inodes with a non-zero lookup count may receive request from
|
||||
* the kernel even after calls to unlink, rmdir or (when
|
||||
* overwriting an existing file) rename. Filesystems must handle
|
||||
* such requests properly and it is recommended to defer removal
|
||||
* of the inode until the lookup count reaches zero. Calls to
|
||||
* unlink, remdir or rename will be followed closely by forget
|
||||
* unless the file or directory is open, in which case the
|
||||
* kernel issues forget only after the release or releasedir
|
||||
* calls.
|
||||
*
|
||||
* Note that if a file system will be exported over NFS the
|
||||
* inodes lifetime must extend even beyond forget. See the
|
||||
* generation field in struct fuse_entry_param above.
|
||||
*
|
||||
* On unmount the lookup count for all inodes implicitly drops
|
||||
* to zero. It is not guaranteed that the file system will
|
||||
* receive corresponding forget messages for the affected
|
||||
* inodes.
|
||||
*
|
||||
* Valid replies:
|
||||
* fuse_reply_none
|
||||
*
|
||||
* @param req request handle
|
||||
* @param ino the inode number
|
||||
* @param nlookup the number of lookups to forget
|
||||
*/
|
||||
void (*forget)(fuse_req_t req, uint64_t ino, uint64_t nlookup);
|
||||
|
||||
/**
|
||||
* Get file attributes
|
||||
@@ -237,8 +234,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param fi for future use, currently always NULL
|
||||
*/
|
||||
void (*getattr) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*getattr)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Set file attributes
|
||||
@@ -267,8 +264,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* Changed in version 2.5:
|
||||
* file information filled in for ftruncate
|
||||
*/
|
||||
void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
||||
int to_set, struct fuse_file_info *fi);
|
||||
void (*setattr)(fuse_req_t req, uint64_t ino, struct stat *attr,
|
||||
int to_set, fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Read symbolic link
|
||||
@@ -280,7 +277,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param req request handle
|
||||
* @param ino the inode number
|
||||
*/
|
||||
void (*readlink) (fuse_req_t req, fuse_ino_t ino);
|
||||
void (*readlink)(fuse_req_t req, uint64_t ino);
|
||||
|
||||
/**
|
||||
* Create file node
|
||||
@@ -298,8 +295,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param mode file type and mode with which to create the new file
|
||||
* @param rdev the device number (only valid if created file is a device)
|
||||
*/
|
||||
void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode, dev_t rdev);
|
||||
void (*mknod)(fuse_req_t req, uint64_t parent, const char *name,
|
||||
mode_t mode, dev_t rdev);
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
@@ -313,8 +310,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param name to create
|
||||
* @param mode with which to create the new file
|
||||
*/
|
||||
void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode);
|
||||
void (*mkdir)(fuse_req_t req, uint64_t parent, const char *name,
|
||||
mode_t mode);
|
||||
|
||||
/**
|
||||
* Remove a file
|
||||
@@ -331,7 +328,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param parent inode number of the parent directory
|
||||
* @param name to remove
|
||||
*/
|
||||
void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*unlink)(fuse_req_t req, uint64_t parent, const char *name);
|
||||
|
||||
/**
|
||||
* Remove a directory
|
||||
@@ -348,7 +345,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param parent inode number of the parent directory
|
||||
* @param name to remove
|
||||
*/
|
||||
void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*rmdir)(fuse_req_t req, uint64_t parent, const char *name);
|
||||
|
||||
/**
|
||||
* Create a symbolic link
|
||||
@@ -362,8 +359,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param parent inode number of the parent directory
|
||||
* @param name to create
|
||||
*/
|
||||
void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
|
||||
const char *name);
|
||||
void (*symlink)(fuse_req_t req, const char *link, uint64_t parent,
|
||||
const char *name);
|
||||
|
||||
/** Rename a file
|
||||
*
|
||||
@@ -382,8 +379,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param newparent inode number of the new parent directory
|
||||
* @param newname new name
|
||||
*/
|
||||
void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
fuse_ino_t newparent, const char *newname);
|
||||
void (*rename)(fuse_req_t req, uint64_t parent, const char *name,
|
||||
uint64_t newparent, const char *newname);
|
||||
|
||||
/**
|
||||
* Create a hard link
|
||||
@@ -397,8 +394,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param newparent inode number of the new parent directory
|
||||
* @param newname new name to create
|
||||
*/
|
||||
void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
||||
const char *newname);
|
||||
void (*link)(fuse_req_t req, uint64_t ino, uint64_t newparent,
|
||||
const char *newname);
|
||||
|
||||
/**
|
||||
* Open a file
|
||||
@@ -425,8 +422,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*open) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*open)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Read data
|
||||
@@ -453,8 +450,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param off offset to read from
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
void (*read)(fuse_req_t req, uint64_t ino, size_t size, off_t off,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Write data
|
||||
@@ -479,8 +476,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param off offset to write to
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
|
||||
size_t size, off_t off, struct fuse_file_info *fi);
|
||||
void (*write)(fuse_req_t req, uint64_t ino, const char *buf,
|
||||
size_t size, off_t off, fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Flush method
|
||||
@@ -511,8 +508,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*flush) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*flush)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Release an open file
|
||||
@@ -538,8 +535,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*release) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*release)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Synchronize file contents
|
||||
@@ -555,8 +552,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param datasync flag indicating if only data should be flushed
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
void (*fsync)(fuse_req_t req, uint64_t ino, int datasync,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Open a directory
|
||||
@@ -579,8 +576,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*opendir) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*opendir)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Read directory
|
||||
@@ -603,12 +600,12 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param off offset to continue reading the directory stream
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info *llffi);
|
||||
void (*readdir)(fuse_req_t req, uint64_t ino, size_t size, off_t off,
|
||||
fuse_file_info_t *llffi);
|
||||
|
||||
void (*readdir_plus)(fuse_req_t req, fuse_ino_t ino,
|
||||
void (*readdir_plus)(fuse_req_t req, uint64_t ino,
|
||||
size_t size, off_t off,
|
||||
struct fuse_file_info *ffi);
|
||||
fuse_file_info_t *ffi);
|
||||
|
||||
/**
|
||||
* Release an open directory
|
||||
@@ -626,8 +623,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*releasedir)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Synchronize directory contents
|
||||
@@ -646,8 +643,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param datasync flag indicating if only data should be flushed
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
void (*fsyncdir)(fuse_req_t req, uint64_t ino, int datasync,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Get file system statistics
|
||||
@@ -659,7 +656,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param req request handle
|
||||
* @param ino the inode number, zero means "undefined"
|
||||
*/
|
||||
void (*statfs) (fuse_req_t req, fuse_ino_t ino);
|
||||
void (*statfs)(fuse_req_t req, uint64_t ino);
|
||||
|
||||
/**
|
||||
* Set an extended attribute
|
||||
@@ -667,8 +664,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* Valid replies:
|
||||
* fuse_reply_err
|
||||
*/
|
||||
void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
const char *value, size_t size, int flags);
|
||||
void (*setxattr)(fuse_req_t req, uint64_t ino, const char *name,
|
||||
const char *value, size_t size, int flags);
|
||||
|
||||
/**
|
||||
* Get an extended attribute
|
||||
@@ -693,8 +690,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param name of the extended attribute
|
||||
* @param size maximum size of the value to send
|
||||
*/
|
||||
void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
size_t size);
|
||||
void (*getxattr)(fuse_req_t req, uint64_t ino, const char *name,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* List extended attribute names
|
||||
@@ -719,7 +716,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param size maximum size of the list to send
|
||||
*/
|
||||
void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
|
||||
void (*listxattr)(fuse_req_t req, uint64_t ino, size_t size);
|
||||
|
||||
/**
|
||||
* Remove an extended attribute
|
||||
@@ -731,7 +728,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param name of the extended attribute
|
||||
*/
|
||||
void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
|
||||
void (*removexattr)(fuse_req_t req, uint64_t ino, const char *name);
|
||||
|
||||
/**
|
||||
* Check file access permissions
|
||||
@@ -751,7 +748,7 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param ino the inode number
|
||||
* @param mask requested access mode
|
||||
*/
|
||||
void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
|
||||
void (*access)(fuse_req_t req, uint64_t ino, int mask);
|
||||
|
||||
/**
|
||||
* Create and open a file
|
||||
@@ -786,8 +783,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param mode file type and mode with which to create the new file
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode, struct fuse_file_info *fi);
|
||||
void (*create)(fuse_req_t req, uint64_t parent, const char *name,
|
||||
mode_t mode, fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Test for a POSIX file lock
|
||||
@@ -803,8 +800,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param fi file information
|
||||
* @param lock the region/type to test
|
||||
*/
|
||||
void (*getlk) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi, struct flock *lock);
|
||||
void (*getlk)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi, struct flock *lock);
|
||||
|
||||
/**
|
||||
* Acquire, modify or release a POSIX file lock
|
||||
@@ -830,9 +827,9 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param lock the region/type to set
|
||||
* @param sleep locking operation may sleep
|
||||
*/
|
||||
void (*setlk) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi,
|
||||
struct flock *lock, int sleep);
|
||||
void (*setlk)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi,
|
||||
struct flock *lock, int sleep);
|
||||
|
||||
/**
|
||||
* Map block index within file to block index within device
|
||||
@@ -851,8 +848,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param blocksize unit of block index
|
||||
* @param idx block index within file
|
||||
*/
|
||||
void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
|
||||
uint64_t idx);
|
||||
void (*bmap)(fuse_req_t req, uint64_t ino, size_t blocksize,
|
||||
uint64_t idx);
|
||||
|
||||
/**
|
||||
* Ioctl
|
||||
@@ -881,9 +878,9 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param in_bufsz number of fetched bytes
|
||||
* @param out_bufsz maximum size of output data
|
||||
*/
|
||||
void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned long cmd, void *arg,
|
||||
struct fuse_file_info *fi, unsigned flags,
|
||||
const void *in_buf, uint32_t in_bufsz, uint32_t out_bufsz);
|
||||
void (*ioctl)(fuse_req_t req, uint64_t ino, unsigned long cmd, void *arg,
|
||||
fuse_file_info_t *fi, unsigned flags,
|
||||
const void *in_buf, uint32_t in_bufsz, uint32_t out_bufsz);
|
||||
|
||||
/**
|
||||
* Poll for IO readiness
|
||||
@@ -911,8 +908,10 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param fi file information
|
||||
* @param ph poll handle to be used for notification
|
||||
*/
|
||||
void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
|
||||
struct fuse_pollhandle *ph);
|
||||
void (*poll)(fuse_req_t req,
|
||||
uint64_t ino,
|
||||
fuse_file_info_t *fi,
|
||||
fuse_pollhandle_t *ph);
|
||||
|
||||
/**
|
||||
* Write data made available in a buffer
|
||||
@@ -940,9 +939,9 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param off offset to write to
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*write_buf) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_bufvec *bufv, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
void (*write_buf)(fuse_req_t req, uint64_t ino,
|
||||
struct fuse_bufvec *bufv, off_t off,
|
||||
fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Callback function for the retrieve request
|
||||
@@ -958,8 +957,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param offset the offset supplied to fuse_lowlevel_notify_retrieve()
|
||||
* @param bufv the buffer containing the returned data
|
||||
*/
|
||||
void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino,
|
||||
off_t offset, struct fuse_bufvec *bufv);
|
||||
void (*retrieve_reply)(fuse_req_t req, void *cookie, uint64_t ino,
|
||||
off_t offset, struct fuse_bufvec *bufv);
|
||||
|
||||
/**
|
||||
* Forget about multiple inodes
|
||||
@@ -974,8 +973,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
*
|
||||
* @param req request handle
|
||||
*/
|
||||
void (*forget_multi) (fuse_req_t req, size_t count,
|
||||
struct fuse_forget_data *forgets);
|
||||
void (*forget_multi)(fuse_req_t req, size_t count,
|
||||
struct fuse_forget_data *forgets);
|
||||
|
||||
/**
|
||||
* Acquire, modify or release a BSD file lock
|
||||
@@ -994,8 +993,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param fi file information
|
||||
* @param op the locking operation, see flock(2)
|
||||
*/
|
||||
void (*flock) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi, int op);
|
||||
void (*flock)(fuse_req_t req, uint64_t ino,
|
||||
fuse_file_info_t *fi, int op);
|
||||
|
||||
/**
|
||||
* Allocate requested space. If this function returns success then
|
||||
@@ -1014,8 +1013,8 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param mode determines the operation to be performed on the given range,
|
||||
* see fallocate(2)
|
||||
*/
|
||||
void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
|
||||
off_t offset, off_t length, struct fuse_file_info *fi);
|
||||
void (*fallocate)(fuse_req_t req, uint64_t ino, int mode,
|
||||
off_t offset, off_t length, fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Copy a range of data from one file to another
|
||||
@@ -1053,15 +1052,15 @@ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
* @param len maximum size of the data to copy
|
||||
* @param flags passed along with the copy_file_range() syscall
|
||||
*/
|
||||
void (*copy_file_range)(fuse_req_t req,
|
||||
fuse_ino_t ino_in,
|
||||
off_t off_in,
|
||||
struct fuse_file_info *fi_in,
|
||||
fuse_ino_t ino_out,
|
||||
off_t off_out,
|
||||
struct fuse_file_info *fi_out,
|
||||
size_t len,
|
||||
int flags);
|
||||
void (*copy_file_range)(fuse_req_t req,
|
||||
uint64_t ino_in,
|
||||
off_t off_in,
|
||||
fuse_file_info_t *fi_in,
|
||||
uint64_t ino_out,
|
||||
off_t off_out,
|
||||
fuse_file_info_t *fi_out,
|
||||
size_t len,
|
||||
int flags);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1122,7 +1121,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e);
|
||||
* @return zero for success, -errno for failure to send reply
|
||||
*/
|
||||
int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
||||
const struct fuse_file_info *fi);
|
||||
const fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Reply with attributes
|
||||
@@ -1164,7 +1163,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, const struct fuse_file_info *fi);
|
||||
int fuse_reply_open(fuse_req_t req, const fuse_file_info_t *fi);
|
||||
|
||||
/**
|
||||
* Reply with number of bytes written
|
||||
@@ -1331,7 +1330,7 @@ int fuse_reply_poll(fuse_req_t req, unsigned revents);
|
||||
*
|
||||
* @param ph poll handle to notify IO readiness event for
|
||||
*/
|
||||
int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
|
||||
int fuse_lowlevel_notify_poll(fuse_pollhandle_t *ph);
|
||||
|
||||
/**
|
||||
* Notify to invalidate cache for an inode
|
||||
@@ -1343,7 +1342,7 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph);
|
||||
* @param len the amount of cache to invalidate or 0 for all
|
||||
* @return zero for success, -errno for failure
|
||||
*/
|
||||
int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
|
||||
int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, uint64_t ino,
|
||||
off_t off, off_t len);
|
||||
|
||||
/**
|
||||
@@ -1360,7 +1359,7 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_chan *ch, fuse_ino_t ino,
|
||||
* @param namelen strlen() of file name
|
||||
* @return zero for success, -errno for failure
|
||||
*/
|
||||
int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
|
||||
int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, uint64_t parent,
|
||||
const char *name, size_t namelen);
|
||||
|
||||
/**
|
||||
@@ -1380,7 +1379,7 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_chan *ch, fuse_ino_t parent,
|
||||
* @return zero for success, -errno for failure
|
||||
*/
|
||||
int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
|
||||
fuse_ino_t parent, fuse_ino_t child,
|
||||
uint64_t parent, uint64_t child,
|
||||
const char *name, size_t namelen);
|
||||
|
||||
/**
|
||||
@@ -1404,7 +1403,7 @@ int fuse_lowlevel_notify_delete(struct fuse_chan *ch,
|
||||
* @param flags flags controlling the copy
|
||||
* @return zero for success, -errno for failure
|
||||
*/
|
||||
int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
|
||||
int fuse_lowlevel_notify_store(struct fuse_chan *ch, uint64_t ino,
|
||||
off_t offset, struct fuse_bufvec *bufv,
|
||||
enum fuse_buf_copy_flags flags);
|
||||
/**
|
||||
@@ -1432,7 +1431,7 @@ int fuse_lowlevel_notify_store(struct fuse_chan *ch, fuse_ino_t ino,
|
||||
* @param cookie user data to supply to the reply callback
|
||||
* @return zero for success, -errno for failure
|
||||
*/
|
||||
int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, fuse_ino_t ino,
|
||||
int fuse_lowlevel_notify_retrieve(struct fuse_chan *ch, uint64_t ino,
|
||||
size_t size, off_t offset, void *cookie);
|
||||
|
||||
|
||||
@@ -1480,36 +1479,6 @@ const struct fuse_ctx *fuse_req_ctx(fuse_req_t req);
|
||||
*/
|
||||
int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]);
|
||||
|
||||
/**
|
||||
* Callback function for an interrupt
|
||||
*
|
||||
* @param req interrupted request
|
||||
* @param data user data
|
||||
*/
|
||||
typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data);
|
||||
|
||||
/**
|
||||
* Register/unregister callback for an interrupt
|
||||
*
|
||||
* If an interrupt has already happened, then the callback function is
|
||||
* called from within this function, hence it's not possible for
|
||||
* interrupts to be lost.
|
||||
*
|
||||
* @param req request handle
|
||||
* @param func the callback function or NULL for unregister
|
||||
* @param data user data passed to the callback function
|
||||
*/
|
||||
void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Check if a request has already been interrupted
|
||||
*
|
||||
* @param req request handle
|
||||
* @return 1 if the request has been interrupted, 0 otherwise
|
||||
*/
|
||||
int fuse_req_interrupted(fuse_req_t req);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Filesystem setup *
|
||||
* ----------------------------------------------------------- */
|
||||
@@ -1549,8 +1518,8 @@ struct fuse_session_ops
|
||||
* @param len request length
|
||||
* @param ch channel on which the request was received
|
||||
*/
|
||||
void (*process) (void *data, const char *buf, size_t len,
|
||||
struct fuse_chan *ch);
|
||||
void (*process)(void *data, const char *buf, size_t len,
|
||||
struct fuse_chan *ch);
|
||||
|
||||
/**
|
||||
* Hook for session exit and reset (optional)
|
||||
@@ -1558,7 +1527,7 @@ struct fuse_session_ops
|
||||
* @param data user data passed to fuse_session_new()
|
||||
* @param val exited status (1 - exited, 0 - not exited)
|
||||
*/
|
||||
void (*exit) (void *data, int val);
|
||||
void (*exit)(void *data, int val);
|
||||
|
||||
/**
|
||||
* Hook for querying the current exited status (optional)
|
||||
@@ -1566,14 +1535,14 @@ struct fuse_session_ops
|
||||
* @param data user data passed to fuse_session_new()
|
||||
* @return 1 if exited, 0 if not exited
|
||||
*/
|
||||
int (*exited) (void *data);
|
||||
int (*exited)(void *data);
|
||||
|
||||
/**
|
||||
* Hook for cleaning up the channel on destroy (optional)
|
||||
*
|
||||
* @param data user data passed to fuse_session_new()
|
||||
*/
|
||||
void (*destroy) (void *data);
|
||||
void (*destroy)(void *data);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
#define kv_pop(v) ((v).a[--(v).n])
|
||||
#define kv_size(v) ((v).n)
|
||||
#define kv_max(v) ((v).m)
|
||||
#define kv_first(v) (kv_A(v,0))
|
||||
#define kv_last(v) (kv_A(v,kv_size(v)-1))
|
||||
#define kv_end(v) (kv_A(v,kv_size(v)))
|
||||
#define kv_delete(v,i) (kv_A(v,i) = kv_pop(v))
|
||||
|
||||
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "crc32b.h"
|
||||
|
||||
static
|
||||
const
|
||||
crc32b_t CRC32BTABLE[256] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
crc32b_t
|
||||
crc32b_start(void)
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
crc32b_t
|
||||
crc32b_continue(const void *buf_,
|
||||
const crc32b_t len_,
|
||||
const crc32b_t crc_)
|
||||
{
|
||||
int i;
|
||||
char *buf;
|
||||
crc32b_t crc;
|
||||
|
||||
crc = crc_;
|
||||
buf = (char*)buf_;
|
||||
for(i = 0; i < len_; i++)
|
||||
{
|
||||
crc = (CRC32BTABLE[(crc ^ buf[i]) & 0xFFL] ^ (crc >> 8));
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
crc32b_t
|
||||
crc32b_finish(const crc32b_t crc_)
|
||||
{
|
||||
return (crc_ ^ 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
crc32b_t
|
||||
crc32b(const void *buf_,
|
||||
const crc32b_t len_)
|
||||
{
|
||||
crc32b_t crc;
|
||||
|
||||
crc = crc32b_start();
|
||||
crc = crc32b_continue(buf_,len_,crc);
|
||||
crc = crc32b_finish(crc);
|
||||
|
||||
return crc;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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.
|
||||
*/
|
||||
|
||||
#ifndef CRC32B_H_INCLUDED
|
||||
#define CRC32B_H_INCLUDED
|
||||
|
||||
typedef unsigned int crc32b_t;
|
||||
|
||||
crc32b_t crc32b_start(void);
|
||||
crc32b_t crc32b_continue(const void *buf,
|
||||
const crc32b_t len,
|
||||
const crc32b_t crc);
|
||||
crc32b_t crc32b_finish(const crc32b_t crc);
|
||||
|
||||
crc32b_t crc32b(const void *buf, crc32b_t len);
|
||||
|
||||
#endif
|
||||
+1278
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "fuse_kernel.h"
|
||||
|
||||
void debug_fuse_open_out(const uint64_t unique,
|
||||
const struct fuse_open_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_init_in(const struct fuse_init_in *arg);
|
||||
void debug_fuse_init_out(const uint64_t unique,
|
||||
const struct fuse_init_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_entry_out(const uint64_t unique,
|
||||
const struct fuse_entry_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_attr_out(const uint64_t unique,
|
||||
const struct fuse_attr_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_entry_open_out(const uint64_t unique,
|
||||
const struct fuse_entry_out *earg,
|
||||
const struct fuse_open_out *oarg);
|
||||
void debug_fuse_readlink(const uint64_t unique,
|
||||
const char *linkname);
|
||||
void debug_fuse_write_out(const uint64_t unique,
|
||||
const struct fuse_write_out *arg);
|
||||
void debug_fuse_statfs_out(const uint64_t unique,
|
||||
const struct fuse_statfs_out *arg);
|
||||
void debug_fuse_getxattr_out(const uint64_t unique,
|
||||
const struct fuse_getxattr_out *arg);
|
||||
void debug_fuse_lk_out(const uint64_t unique,
|
||||
const struct fuse_lk_out *arg);
|
||||
void debug_fuse_bmap_out(const uint64_t unique,
|
||||
const struct fuse_bmap_out *arg);
|
||||
void debug_fuse_in_header(const struct fuse_in_header *hdr);
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ROUND_UP(N,S) ((((N) + (S) - 1) / (S)) * (S))
|
||||
|
||||
typedef kvec_t(void*) slab_kvec_t;
|
||||
|
||||
typedef struct mem_stack_t mem_stack_t;
|
||||
struct mem_stack_t
|
||||
{
|
||||
mem_stack_t *next;
|
||||
};
|
||||
|
||||
typedef struct fmp_t fmp_t;
|
||||
struct fmp_t
|
||||
{
|
||||
mem_stack_t *objs;
|
||||
slab_kvec_t slabs;
|
||||
uint64_t avail_objs;
|
||||
uint64_t obj_size;
|
||||
uint64_t page_size;
|
||||
uint64_t slab_size;
|
||||
};
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_page_size()
|
||||
{
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_init(fmp_t *fmp_,
|
||||
const uint64_t obj_size_,
|
||||
const uint64_t page_multiple_)
|
||||
{
|
||||
kv_init(fmp_->slabs);
|
||||
fmp_->objs = NULL;
|
||||
fmp_->avail_objs = 0;
|
||||
fmp_->obj_size = ROUND_UP(obj_size_,sizeof(void*));
|
||||
fmp_->page_size = fmp_page_size();
|
||||
fmp_->slab_size = (fmp_->page_size * page_multiple_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_slab_count(fmp_t *fmp_)
|
||||
{
|
||||
return kv_size(fmp_->slabs);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_slab_alloc_posix_memalign(fmp_t *fmp_)
|
||||
{
|
||||
int rv;
|
||||
void *mem;
|
||||
const size_t alignment = fmp_->page_size;
|
||||
const size_t size = fmp_->slab_size;
|
||||
|
||||
rv = posix_memalign(&mem,alignment,size);
|
||||
if(rv != 0)
|
||||
return NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_slab_alloc_mmap(fmp_t *fmp_)
|
||||
{
|
||||
void *mem;
|
||||
void *address = NULL;
|
||||
const size_t length = fmp_->slab_size;
|
||||
const int protect = PROT_READ|PROT_WRITE;
|
||||
const int flags = MAP_PRIVATE|MAP_ANONYMOUS;
|
||||
const int filedes = -1;
|
||||
const off_t offset = 0;
|
||||
|
||||
mem = mmap(address,length,protect,flags,filedes,offset);
|
||||
if(mem == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_slab_free_posix_memalign(fmp_t* fmp_,
|
||||
void *mem_)
|
||||
{
|
||||
(void)fmp_;
|
||||
free(mem_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_slab_free_mmap(fmp_t* fmp_,
|
||||
void *mem_)
|
||||
{
|
||||
void *addr = mem_;
|
||||
size_t length = fmp_->slab_size;
|
||||
|
||||
(void)munmap(addr,length);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
fmp_slab_alloc(fmp_t *fmp_)
|
||||
{
|
||||
char *i;
|
||||
void *mem;
|
||||
|
||||
mem = fmp_slab_alloc_mmap(fmp_);
|
||||
if(mem == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kv_push(void*,fmp_->slabs,mem);
|
||||
|
||||
i = ((char*)mem + fmp_->slab_size - fmp_->obj_size);
|
||||
while(i >= (char*)mem)
|
||||
{
|
||||
mem_stack_t *obj = (mem_stack_t*)i;
|
||||
|
||||
obj->next = fmp_->objs;
|
||||
fmp_->objs = obj;
|
||||
fmp_->avail_objs++;
|
||||
|
||||
i -= fmp_->obj_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_alloc(fmp_t *fmp_)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
if(fmp_->objs == NULL)
|
||||
fmp_slab_alloc(fmp_);
|
||||
if(fmp_->objs == NULL)
|
||||
return NULL;
|
||||
|
||||
rv = fmp_->objs;
|
||||
|
||||
fmp_->objs = fmp_->objs->next;
|
||||
fmp_->avail_objs--;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_calloc(fmp_t *fmp_)
|
||||
{
|
||||
void *obj;
|
||||
|
||||
obj = fmp_alloc(fmp_);
|
||||
if(obj == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(obj,0,fmp_->obj_size);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_free(fmp_t *fmp_,
|
||||
void *obj_)
|
||||
{
|
||||
mem_stack_t *obj = (mem_stack_t*)obj_;
|
||||
|
||||
obj->next = fmp_->objs;
|
||||
fmp_->objs = obj;
|
||||
fmp_->avail_objs++;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_clear(fmp_t *fmp_)
|
||||
{
|
||||
while(kv_size(fmp_->slabs))
|
||||
{
|
||||
void *slab = kv_pop(fmp_->slabs);
|
||||
|
||||
fmp_slab_free_mmap(fmp_,slab);
|
||||
}
|
||||
|
||||
fmp_->objs = NULL;
|
||||
fmp_->avail_objs = 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_destroy(fmp_t *fmp_)
|
||||
{
|
||||
fmp_clear(fmp_);
|
||||
kv_destroy(fmp_->slabs);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_avail_objs(fmp_t *fmp_)
|
||||
{
|
||||
return fmp_->avail_objs;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_objs_per_slab(fmp_t *fmp_)
|
||||
{
|
||||
return (fmp_->slab_size / fmp_->obj_size);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_objs_in_slab(fmp_t *fmp_,
|
||||
void *slab_)
|
||||
{
|
||||
char *slab;
|
||||
uint64_t objs_per_slab;
|
||||
uint64_t objs_in_slab;
|
||||
|
||||
slab = (char*)slab_;
|
||||
objs_in_slab = 0;
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
for(mem_stack_t *stack = fmp_->objs; stack != NULL; stack = stack->next)
|
||||
{
|
||||
char *obj = (char*)stack;
|
||||
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
|
||||
objs_in_slab++;
|
||||
if(objs_in_slab >= objs_per_slab)
|
||||
break;
|
||||
}
|
||||
|
||||
return objs_in_slab;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_remove_objs_in_slab(fmp_t *fmp_,
|
||||
void *slab_)
|
||||
{
|
||||
char *slab;
|
||||
uint64_t objs_per_slab;
|
||||
uint64_t objs_in_slab;
|
||||
mem_stack_t **p;
|
||||
|
||||
p = &fmp_->objs;
|
||||
slab = (char*)slab_;
|
||||
objs_in_slab = 0;
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
while((*p) != NULL)
|
||||
{
|
||||
char *obj = (char*)*p;
|
||||
|
||||
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
|
||||
{
|
||||
objs_in_slab++;
|
||||
*p = (*p)->next;
|
||||
fmp_->avail_objs--;
|
||||
|
||||
if(objs_in_slab >= objs_per_slab)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
p = &(*p)->next;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
fmp_gc_slab(fmp_t *fmp_,
|
||||
uint64_t slab_idx_)
|
||||
{
|
||||
char *slab;
|
||||
uint64_t objs_in_slab;
|
||||
uint64_t objs_per_slab;
|
||||
|
||||
slab_idx_ = (slab_idx_ % kv_size(fmp_->slabs));
|
||||
|
||||
slab = kv_A(fmp_->slabs,slab_idx_);
|
||||
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
objs_in_slab = fmp_objs_in_slab(fmp_,slab);
|
||||
if(objs_in_slab != objs_per_slab)
|
||||
return 0;
|
||||
|
||||
fmp_remove_objs_in_slab(fmp_,slab);
|
||||
kv_delete(fmp_->slabs,slab_idx_);
|
||||
fmp_slab_free_mmap(fmp_,slab);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
fmp_gc(fmp_t *fmp_)
|
||||
{
|
||||
uint64_t slab_idx;
|
||||
|
||||
slab_idx = rand();
|
||||
|
||||
return fmp_gc_slab(fmp_,slab_idx);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
double
|
||||
fmp_slab_usage_ratio(fmp_t *fmp_)
|
||||
{
|
||||
double avail_objs;
|
||||
double objs_per_slab;
|
||||
double nums_of_slabs;
|
||||
|
||||
avail_objs = fmp_->avail_objs;
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
nums_of_slabs = kv_size(fmp_->slabs);
|
||||
|
||||
return (avail_objs / (objs_per_slab * nums_of_slabs));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_total_allocated_memory(fmp_t *fmp_)
|
||||
{
|
||||
return (fmp_->slab_size * kv_size(fmp_->slabs));
|
||||
}
|
||||
+2462
-2701
File diff suppressed because it is too large
Load Diff
+33
-37
@@ -19,6 +19,14 @@
|
||||
/* 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_)
|
||||
@@ -57,16 +65,15 @@ int
|
||||
fuse_dirents_buf_resize(fuse_dirents_t *d_,
|
||||
uint64_t size_)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if((d_->data_len + size_) >= d_->buf_len)
|
||||
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
|
||||
{
|
||||
p = realloc(d_->buf,(d_->buf_len * 2));
|
||||
if(p == NULL)
|
||||
return -errno;
|
||||
uint64_t new_size;
|
||||
|
||||
d_->buf = p;
|
||||
d_->buf_len *= 2;
|
||||
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;
|
||||
@@ -87,9 +94,8 @@ fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
|
||||
d_->data_len += size;
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
@@ -109,9 +115,8 @@ fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
|
||||
d_->data_len += size;
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
@@ -170,8 +175,8 @@ fuse_dirent_find(fuse_dirents_t *d_,
|
||||
if(d_->type != NORMAL)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_dirent_t*)&d_->buf[0];
|
||||
end = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
cur = (fuse_dirent_t*)&kv_first(d_->data);
|
||||
end = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->ino == ino_)
|
||||
@@ -193,8 +198,8 @@ fuse_direntplus_find(fuse_dirents_t *d_,
|
||||
if(d_->type != PLUS)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_direntplus_t*)&d_->buf[0];
|
||||
end = (fuse_direntplus_t*)&d_->buf[d_->data_len];
|
||||
cur = (fuse_direntplus_t*)&kv_first(d_->data);
|
||||
end = (fuse_direntplus_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->dirent.ino == ino_)
|
||||
@@ -251,7 +256,7 @@ fuse_dirents_add(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->d_ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->d_type;
|
||||
@@ -285,7 +290,7 @@ fuse_dirents_add_plus(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
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;
|
||||
@@ -321,7 +326,7 @@ fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->type;
|
||||
@@ -355,7 +360,7 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->dirent.ino = dirent_->ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->type;
|
||||
@@ -371,25 +376,21 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
void
|
||||
fuse_dirents_reset(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
kv_size(d_->data) = 0;
|
||||
kv_size(d_->offs) = 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_init(fuse_dirents_t *d_)
|
||||
{
|
||||
void *buf;
|
||||
d_->type = UNSET;
|
||||
|
||||
buf = calloc(DEFAULT_SIZE,1);
|
||||
if(buf == NULL)
|
||||
kv_init(d_->data);
|
||||
kv_resize(char,d_->data,DEFAULT_SIZE);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d_->buf = buf;
|
||||
d_->buf_len = DEFAULT_SIZE;
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_init(d_->offs);
|
||||
kv_resize(uint32_t,d_->offs,64);
|
||||
kv_push(uint32_t,d_->offs,0);
|
||||
@@ -400,11 +401,6 @@ fuse_dirents_init(fuse_dirents_t *d_)
|
||||
void
|
||||
fuse_dirents_free(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->buf_len = 0;
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_destroy(d_->data);
|
||||
kv_destroy(d_->offs);
|
||||
|
||||
free(d_->buf);
|
||||
}
|
||||
|
||||
+7
-22
@@ -35,30 +35,19 @@ struct fuse_req
|
||||
{
|
||||
struct fuse_ll *f;
|
||||
uint64_t unique;
|
||||
int ctr;
|
||||
pthread_mutex_t lock;
|
||||
struct fuse_ctx ctx;
|
||||
struct fuse_chan *ch;
|
||||
int interrupted;
|
||||
unsigned int ioctl_64bit : 1;
|
||||
union {
|
||||
struct {
|
||||
uint64_t unique;
|
||||
} i;
|
||||
struct {
|
||||
fuse_interrupt_func_t func;
|
||||
void *data;
|
||||
} ni;
|
||||
} u;
|
||||
struct fuse_req *next;
|
||||
struct fuse_req *prev;
|
||||
};
|
||||
|
||||
struct fuse_notify_req
|
||||
{
|
||||
uint64_t unique;
|
||||
void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t,
|
||||
const void *, const struct fuse_buf *);
|
||||
void (*reply)(struct fuse_notify_req *,
|
||||
fuse_req_t,
|
||||
uint64_t,
|
||||
const void *,
|
||||
const struct fuse_buf *);
|
||||
struct fuse_notify_req *next;
|
||||
struct fuse_notify_req *prev;
|
||||
};
|
||||
@@ -66,7 +55,6 @@ struct fuse_notify_req
|
||||
struct fuse_ll
|
||||
{
|
||||
int debug;
|
||||
int allow_root;
|
||||
int no_remote_posix_lock;
|
||||
int no_remote_flock;
|
||||
int big_writes;
|
||||
@@ -81,8 +69,6 @@ struct fuse_ll
|
||||
void *userdata;
|
||||
uid_t owner;
|
||||
struct fuse_conn_info conn;
|
||||
struct fuse_req list;
|
||||
struct fuse_req interrupts;
|
||||
pthread_mutex_t lock;
|
||||
int got_destroy;
|
||||
pthread_key_t pipe_key;
|
||||
@@ -100,7 +86,7 @@ struct fuse_cmd
|
||||
|
||||
struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size, void *user_data);
|
||||
size_t op_size);
|
||||
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd);
|
||||
|
||||
@@ -122,7 +108,6 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *fd,
|
||||
void *user_data);
|
||||
int *fd);
|
||||
|
||||
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
|
||||
|
||||
+73
-66
@@ -6,25 +6,26 @@
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_i.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <semaphore.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Environment var controlling the thread stack size */
|
||||
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
|
||||
|
||||
struct fuse_worker {
|
||||
struct fuse_worker
|
||||
{
|
||||
struct fuse_worker *prev;
|
||||
struct fuse_worker *next;
|
||||
pthread_t thread_id;
|
||||
@@ -33,7 +34,8 @@ struct fuse_worker {
|
||||
struct fuse_mt *mt;
|
||||
};
|
||||
|
||||
struct fuse_mt {
|
||||
struct fuse_mt
|
||||
{
|
||||
struct fuse_session *se;
|
||||
struct fuse_chan *prevch;
|
||||
struct fuse_worker main;
|
||||
@@ -42,7 +44,10 @@ struct fuse_mt {
|
||||
int error;
|
||||
};
|
||||
|
||||
static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
|
||||
static
|
||||
void
|
||||
list_add_worker(struct fuse_worker *w,
|
||||
struct fuse_worker *next)
|
||||
{
|
||||
struct fuse_worker *prev = next->prev;
|
||||
w->next = next;
|
||||
@@ -61,38 +66,41 @@ static void list_del_worker(struct fuse_worker *w)
|
||||
|
||||
static int fuse_loop_start_thread(struct fuse_mt *mt);
|
||||
|
||||
static void *fuse_do_work(void *data)
|
||||
static
|
||||
void*
|
||||
fuse_do_work(void *data)
|
||||
{
|
||||
struct fuse_worker *w = (struct fuse_worker *) data;
|
||||
struct fuse_mt *mt = w->mt;
|
||||
|
||||
while (!fuse_session_exited(mt->se)) {
|
||||
struct fuse_chan *ch = mt->prevch;
|
||||
struct fuse_buf fbuf = {
|
||||
.mem = w->buf,
|
||||
.size = w->bufsize,
|
||||
};
|
||||
int res;
|
||||
while(!fuse_session_exited(mt->se))
|
||||
{
|
||||
int res;
|
||||
struct fuse_buf fbuf;
|
||||
struct fuse_chan *ch = mt->prevch;
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
if (res == -EINTR)
|
||||
continue;
|
||||
if (res <= 0) {
|
||||
if (res < 0) {
|
||||
fuse_session_exit(mt->se);
|
||||
mt->error = -1;
|
||||
fbuf = (struct fuse_buf){ .mem = w->buf,
|
||||
.size = w->bufsize };
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
res = fuse_session_receive_buf(mt->se, &fbuf, &ch);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
if(res == -EINTR)
|
||||
continue;
|
||||
if(res <= 0) {
|
||||
if(res < 0) {
|
||||
fuse_session_exit(mt->se);
|
||||
mt->error = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if(mt->exit)
|
||||
return NULL;
|
||||
|
||||
fuse_session_process_buf(mt->se, &fbuf, ch);
|
||||
}
|
||||
|
||||
if (mt->exit)
|
||||
return NULL;
|
||||
|
||||
fuse_session_process_buf(mt->se, &fbuf, ch);
|
||||
}
|
||||
|
||||
sem_post(&mt->finish);
|
||||
|
||||
return NULL;
|
||||
@@ -109,20 +117,16 @@ int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
|
||||
/* Override default stack size */
|
||||
pthread_attr_init(&attr);
|
||||
stack_size = getenv(ENVNAME_THREAD_STACK);
|
||||
if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
|
||||
if(stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
|
||||
fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
|
||||
|
||||
/* Disallow signal reception in worker threads */
|
||||
sigemptyset(&newset);
|
||||
sigaddset(&newset, SIGTERM);
|
||||
sigaddset(&newset, SIGINT);
|
||||
sigaddset(&newset, SIGHUP);
|
||||
sigaddset(&newset, SIGQUIT);
|
||||
pthread_sigmask(SIG_BLOCK, &newset, &oldset);
|
||||
sigfillset(&newset);
|
||||
pthread_sigmask(SIG_BLOCK,&newset,&oldset);
|
||||
res = pthread_create(thread_id, &attr, func, arg);
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
pthread_sigmask(SIG_SETMASK,&oldset,NULL);
|
||||
pthread_attr_destroy(&attr);
|
||||
if (res != 0) {
|
||||
if(res != 0) {
|
||||
fprintf(stderr, "fuse: error creating thread: %s\n",
|
||||
strerror(res));
|
||||
return -1;
|
||||
@@ -135,7 +139,7 @@ static int fuse_loop_start_thread(struct fuse_mt *mt)
|
||||
{
|
||||
int res;
|
||||
struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
|
||||
if (!w) {
|
||||
if(!w) {
|
||||
fprintf(stderr, "fuse: failed to allocate worker structure\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -143,14 +147,14 @@ static int fuse_loop_start_thread(struct fuse_mt *mt)
|
||||
w->bufsize = fuse_chan_bufsize(mt->prevch);
|
||||
w->buf = calloc(w->bufsize,1);
|
||||
w->mt = mt;
|
||||
if (!w->buf) {
|
||||
if(!w->buf) {
|
||||
fprintf(stderr, "fuse: failed to allocate read buffer\n");
|
||||
free(w);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
|
||||
if (res == -1) {
|
||||
if(res == -1) {
|
||||
free(w->buf);
|
||||
free(w);
|
||||
return -1;
|
||||
@@ -177,8 +181,9 @@ static int number_of_threads(void)
|
||||
return 4;
|
||||
}
|
||||
|
||||
int fuse_session_loop_mt(struct fuse_session *se,
|
||||
const int _threads)
|
||||
int
|
||||
fuse_session_loop_mt(struct fuse_session *se_,
|
||||
const int threads_)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
@@ -186,17 +191,17 @@ int fuse_session_loop_mt(struct fuse_session *se,
|
||||
struct fuse_mt mt;
|
||||
struct fuse_worker *w;
|
||||
|
||||
memset(&mt, 0, sizeof(struct fuse_mt));
|
||||
mt.se = se;
|
||||
mt.prevch = fuse_session_next_chan(se, NULL);
|
||||
memset(&mt,0,sizeof(struct fuse_mt));
|
||||
mt.se = se_;
|
||||
mt.prevch = fuse_session_next_chan(se_,NULL);
|
||||
mt.error = 0;
|
||||
mt.main.thread_id = pthread_self();
|
||||
mt.main.prev = mt.main.next = &mt.main;
|
||||
sem_init(&mt.finish, 0, 0);
|
||||
sem_init(&mt.finish,0,0);
|
||||
|
||||
threads = ((_threads > 0) ? _threads : number_of_threads());
|
||||
if(_threads < 0)
|
||||
threads /= -_threads;
|
||||
threads = ((threads_ > 0) ? threads_ : number_of_threads());
|
||||
if(threads_ < 0)
|
||||
threads /= -threads_;
|
||||
if(threads == 0)
|
||||
threads = 1;
|
||||
|
||||
@@ -204,22 +209,24 @@ int fuse_session_loop_mt(struct fuse_session *se,
|
||||
for(i = 0; (i < threads) && !err; i++)
|
||||
err = fuse_loop_start_thread(&mt);
|
||||
|
||||
if (!err) {
|
||||
/* sem_wait() is interruptible */
|
||||
while (!fuse_session_exited(se))
|
||||
sem_wait(&mt.finish);
|
||||
if(!err)
|
||||
{
|
||||
/* sem_wait() is interruptible */
|
||||
while(!fuse_session_exited(se_))
|
||||
sem_wait(&mt.finish);
|
||||
|
||||
for (w = mt.main.next; w != &mt.main; w = w->next)
|
||||
pthread_cancel(w->thread_id);
|
||||
mt.exit = 1;
|
||||
for(w = mt.main.next; w != &mt.main; w = w->next)
|
||||
pthread_cancel(w->thread_id);
|
||||
mt.exit = 1;
|
||||
|
||||
while (mt.main.next != &mt.main)
|
||||
fuse_join_worker(mt.main.next);
|
||||
while(mt.main.next != &mt.main)
|
||||
fuse_join_worker(mt.main.next);
|
||||
|
||||
err = mt.error;
|
||||
}
|
||||
err = mt.error;
|
||||
}
|
||||
|
||||
sem_destroy(&mt.finish);
|
||||
fuse_session_reset(se);
|
||||
fuse_session_reset(se_);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
+1401
-1386
File diff suppressed because it is too large
Load Diff
@@ -112,12 +112,14 @@ int fuse_loop_mt(struct fuse *f)
|
||||
if (f == NULL)
|
||||
return -1;
|
||||
|
||||
int res = fuse_start_cleanup_thread(f);
|
||||
int res = fuse_start_maintenance_thread(f);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
res = fuse_session_loop_mt(fuse_get_session(f),
|
||||
fuse_config_num_threads(f));
|
||||
fuse_stop_cleanup_thread(f);
|
||||
|
||||
fuse_stop_maintenance_thread(f);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "fuse_node.h"
|
||||
|
||||
#include "khash.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <stdio.h> // for debugging
|
||||
|
||||
#define UNKNOWN_INO UINT64_MAX
|
||||
#define ROOT_NODE_ID 0
|
||||
#define ROOT_NODE_NAME "/"
|
||||
|
||||
typedef struct node_idname_t node_idname_t;
|
||||
struct node_idname_t
|
||||
{
|
||||
uint64_t id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static khint_t idname_hash_func(const node_idname_t idname);
|
||||
static int idname_hash_equal(const node_idname_t idname0, const node_idname_t idname1);
|
||||
|
||||
KHASH_INIT(node,node_idname_t,fuse_node_t*,1,idname_hash_func,idname_hash_equal);
|
||||
|
||||
typedef struct fuse_node_hashtable_t fuse_node_hashtable_t;
|
||||
struct fuse_node_hashtable_t
|
||||
{
|
||||
kh_node_t *ht;
|
||||
uint64_t id;
|
||||
uint64_t generation;
|
||||
};
|
||||
|
||||
static
|
||||
inline
|
||||
khint_t
|
||||
idname_hash_func(const node_idname_t idname_)
|
||||
{
|
||||
if(idname_.name == NULL)
|
||||
return idname_.id;
|
||||
return (idname_.id ^ kh_str_hash_func(idname_.name));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
idname_hash_equal(const node_idname_t idname0_,
|
||||
const node_idname_t idname1_)
|
||||
{
|
||||
return ((idname0_.id == idname1_.id) &&
|
||||
((idname0_.name == idname1_.name) ||
|
||||
(strcmp(idname0_.name,idname1_.name) == 0)));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
fuse_node_t*
|
||||
fuse_node_alloc(const uint64_t id_,
|
||||
const char *name_)
|
||||
{
|
||||
fuse_node_t *node;
|
||||
|
||||
node = (fuse_node_t*)calloc(1,sizeof(fuse_node_t));
|
||||
|
||||
node->id = id_;
|
||||
node->name = strdup(name_);
|
||||
node->ref_count = 1;
|
||||
node->lookup_count = 1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fuse_node_free(fuse_node_t *node_)
|
||||
{
|
||||
free(node_->name);
|
||||
free(node_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
rand64()
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = rand();
|
||||
rv <<= 32;
|
||||
rv |= rand();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
node_hashtable_gen_unique_id(fuse_node_hashtable_t *ht_)
|
||||
{
|
||||
do
|
||||
{
|
||||
ht_->id++;
|
||||
if(ht_->id == 0)
|
||||
ht_->generation++;
|
||||
}
|
||||
while((ht_->id == 0) || (ht_->id == UNKNOWN_INO));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
node_hashtable_put_root(fuse_node_hashtable_t *ht_)
|
||||
{
|
||||
int rv;
|
||||
khint_t k;
|
||||
fuse_node_t *root_node;
|
||||
const node_idname_t idname0 = {ROOT_NODE_ID,""};
|
||||
const node_idname_t idname1 = {ROOT_NODE_ID,ROOT_NODE_NAME};
|
||||
|
||||
root_node = fuse_node_alloc(ROOT_NODE_ID,ROOT_NODE_NAME);
|
||||
|
||||
k = kh_put_node(ht_->ht,idname0,&rv);
|
||||
kh_value(ht_->ht,k) = root_node;
|
||||
|
||||
k = kh_put_node(ht_->ht,idname1,&rv);
|
||||
kh_value(ht_->ht,k) = root_node;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
node_hashtable_set_id_gen(fuse_node_hashtable_t *ht_,
|
||||
fuse_node_t *node_)
|
||||
{
|
||||
node_hashtable_gen_unique_id(ht_);
|
||||
node_->id = ht_->id;
|
||||
node_->generation = ht_->generation;
|
||||
}
|
||||
|
||||
fuse_node_hashtable_t*
|
||||
fuse_node_hashtable_init()
|
||||
{
|
||||
fuse_node_hashtable_t *ht;
|
||||
|
||||
ht = (fuse_node_hashtable_t*)calloc(sizeof(fuse_node_hashtable_t),1);
|
||||
if(ht == NULL)
|
||||
return NULL;
|
||||
|
||||
ht->ht = kh_init_node();
|
||||
if(ht->ht == NULL)
|
||||
{
|
||||
free(ht);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
ht->id = 0;
|
||||
ht->generation = rand64();
|
||||
|
||||
node_hashtable_put_root(ht);
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
fuse_node_t*
|
||||
fuse_node_hashtable_put(fuse_node_hashtable_t *ht_,
|
||||
const uint64_t parent_id_,
|
||||
const uint64_t child_id_,
|
||||
const char *child_name_)
|
||||
{
|
||||
int rv;
|
||||
khint_t k;
|
||||
fuse_node_t *child_node;
|
||||
const node_idname_t p_idname = {parent_id_,""};
|
||||
const node_idname_t c0_idname = {child_id_,child_name_};
|
||||
const node_idname_t c1_idname = {parent_id_,child_name_};
|
||||
|
||||
child_node = fuse_node_alloc(child_id_,child_name_);
|
||||
|
||||
k = kh_get_node(ht_->ht,p_idname);
|
||||
child_node->parent = kh_value(ht_->ht,k);
|
||||
child_node->parent->ref_count++;
|
||||
|
||||
k = kh_put_node(ht_->ht,c0_idname,&rv);
|
||||
kh_value(ht_->ht,k) = child_node;
|
||||
|
||||
k = kh_put_node(ht_->ht,c1_idname,&rv);
|
||||
kh_value(ht_->ht,k) = child_node;
|
||||
|
||||
return child_node;
|
||||
}
|
||||
|
||||
fuse_node_t*
|
||||
fuse_node_hashtable_get(fuse_node_hashtable_t *ht_,
|
||||
const uint64_t id_)
|
||||
{
|
||||
return fuse_node_hashtable_get_child(ht_,id_,"");
|
||||
}
|
||||
|
||||
fuse_node_t*
|
||||
fuse_node_hashtable_get_child(fuse_node_hashtable_t *ht_,
|
||||
const uint64_t parent_id_,
|
||||
const char *child_name_)
|
||||
{
|
||||
khint_t k;
|
||||
fuse_node_t *node;
|
||||
const node_idname_t idname = {parent_id_,child_name_};
|
||||
|
||||
k = kh_get_node(ht_->ht,idname);
|
||||
node = ((k != kh_end(ht_->ht)) ?
|
||||
kh_value(ht_->ht,k) :
|
||||
NULL);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_node_hashtable_del(fuse_node_hashtable_t *ht_,
|
||||
fuse_node_t *node_)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_node_t fuse_node_t;
|
||||
struct fuse_node_t
|
||||
{
|
||||
uint64_t id;
|
||||
uint64_t generation;
|
||||
char *name;
|
||||
fuse_node_t *parent;
|
||||
uint32_t ref_count;
|
||||
uint64_t lookup_count;
|
||||
uint64_t open_count;
|
||||
};
|
||||
|
||||
struct fuse_node_hashtable_t;
|
||||
typedef struct fuse_node_hashtable_t fuse_node_hashtable_t;
|
||||
|
||||
|
||||
fuse_node_hashtable_t *fuse_node_hashtable_init();
|
||||
|
||||
fuse_node_t *fuse_node_hashtable_put(fuse_node_hashtable_t *ht,
|
||||
const uint64_t parent_id,
|
||||
const uint64_t child_id,
|
||||
const char *child_name);
|
||||
|
||||
fuse_node_t* fuse_node_hashtable_get(fuse_node_hashtable_t *ht,
|
||||
const uint64_t id);
|
||||
fuse_node_t* fuse_node_hashtable_get_child(fuse_node_hashtable_t *ht,
|
||||
const uint64_t id,
|
||||
const char *name);
|
||||
void fuse_node_hashtable_del(fuse_node_hashtable_t *ht,
|
||||
fuse_node_t *node);
|
||||
|
||||
void fuse_node_hashtable_get_path(fuse_node_hashtable_t *ht,
|
||||
char *buf,
|
||||
uint32_t buflen);
|
||||
+11
-13
@@ -40,8 +40,6 @@ static
|
||||
const
|
||||
struct fuse_opt fuse_helper_opts[] =
|
||||
{
|
||||
FUSE_HELPER_OPT("-d", foreground),
|
||||
FUSE_HELPER_OPT("debug", foreground),
|
||||
FUSE_HELPER_OPT("-f", foreground),
|
||||
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
|
||||
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
|
||||
@@ -290,8 +288,7 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *fd,
|
||||
void *user_data)
|
||||
int *fd)
|
||||
{
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
struct fuse_chan *ch;
|
||||
@@ -309,7 +306,7 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
fuse = fuse_new_common(ch, &args, op, op_size, user_data);
|
||||
fuse = fuse_new_common(ch, &args, op, op_size);
|
||||
fuse_opt_free_args(&args);
|
||||
if (fuse == NULL)
|
||||
goto err_unmount;
|
||||
@@ -338,10 +335,10 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
|
||||
struct fuse *fuse_setup(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
char **mountpoint, void *user_data)
|
||||
char **mountpoint)
|
||||
{
|
||||
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
|
||||
NULL, user_data);
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
|
||||
@@ -360,8 +357,7 @@ void fuse_teardown(struct fuse *fuse, char *mountpoint)
|
||||
}
|
||||
|
||||
static int fuse_main_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
void *user_data)
|
||||
const struct fuse_operations *op, size_t op_size)
|
||||
{
|
||||
struct fuse *fuse;
|
||||
char *mountpoint;
|
||||
@@ -369,7 +365,7 @@ static int fuse_main_common(int argc, char *argv[],
|
||||
|
||||
fuse = fuse_setup_common(argc, argv, op, op_size,
|
||||
&mountpoint,
|
||||
NULL, user_data);
|
||||
NULL);
|
||||
if (fuse == NULL)
|
||||
return 1;
|
||||
|
||||
@@ -382,10 +378,12 @@ static int fuse_main_common(int argc, char *argv[],
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
|
||||
size_t op_size, void *user_data)
|
||||
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, user_data);
|
||||
return fuse_main_common(argc, argv, op, op_size);
|
||||
}
|
||||
|
||||
#undef fuse_main
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
An example:
|
||||
|
||||
#include "khash.h"
|
||||
KHASH_MAP_INIT_INT(32, char)
|
||||
int main() {
|
||||
int ret, is_missing;
|
||||
khiter_t k;
|
||||
khash_t(32) *h = kh_init(32);
|
||||
k = kh_put(32, h, 5, &ret);
|
||||
kh_value(h, k) = 10;
|
||||
k = kh_get(32, h, 10);
|
||||
is_missing = (k == kh_end(h));
|
||||
k = kh_get(32, h, 5);
|
||||
kh_del(32, h, k);
|
||||
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||
kh_destroy(32, h);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2013-05-02 (0.2.8):
|
||||
|
||||
* Use quadratic probing. When the capacity is power of 2, stepping function
|
||||
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
|
||||
hashing on cache performance and is more robust than linear probing.
|
||||
|
||||
In theory, double hashing should be more robust than quadratic probing.
|
||||
However, my implementation is probably not for large hash tables, because
|
||||
the second hash function is closely tied to the first hash function,
|
||||
which reduce the effectiveness of double hashing.
|
||||
|
||||
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
|
||||
|
||||
2011-12-29 (0.2.7):
|
||||
|
||||
* Minor code clean up; no actual effect.
|
||||
|
||||
2011-09-16 (0.2.6):
|
||||
|
||||
* The capacity is a power of 2. This seems to dramatically improve the
|
||||
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||
|
||||
- http://code.google.com/p/ulib/
|
||||
- http://nothings.org/computer/judy/
|
||||
|
||||
* Allow to optionally use linear probing which usually has better
|
||||
performance for random input. Double hashing is still the default as it
|
||||
is more robust to certain non-random input.
|
||||
|
||||
* Added Wang's integer hash function (not used by default). This hash
|
||||
function is more robust to certain non-random input.
|
||||
|
||||
2011-02-14 (0.2.5):
|
||||
|
||||
* Allow to declare global functions.
|
||||
|
||||
2009-09-26 (0.2.4):
|
||||
|
||||
* Improve portability
|
||||
|
||||
2008-09-19 (0.2.3):
|
||||
|
||||
* Corrected the example
|
||||
* Improved interfaces
|
||||
|
||||
2008-09-11 (0.2.2):
|
||||
|
||||
* Improved speed a little in kh_put()
|
||||
|
||||
2008-09-10 (0.2.1):
|
||||
|
||||
* Added kh_clear()
|
||||
* Fixed a compiling error
|
||||
|
||||
2008-09-02 (0.2.0):
|
||||
|
||||
* Changed to token concatenation which increases flexibility.
|
||||
|
||||
2008-08-31 (0.1.2):
|
||||
|
||||
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||
|
||||
2008-08-31 (0.1.1):
|
||||
|
||||
* Added destructor
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AC_KHASH_H
|
||||
#define __AC_KHASH_H
|
||||
|
||||
/*!
|
||||
@header
|
||||
|
||||
Generic hash table library.
|
||||
*/
|
||||
|
||||
#define AC_VERSION_KHASH_H "0.2.8"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* compiler specific configuration */
|
||||
|
||||
#if UINT_MAX == 0xffffffffu
|
||||
typedef unsigned int khint32_t;
|
||||
#elif ULONG_MAX == 0xffffffffu
|
||||
typedef unsigned long khint32_t;
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == ULLONG_MAX
|
||||
typedef unsigned long khint64_t;
|
||||
#else
|
||||
typedef unsigned long long khint64_t;
|
||||
#endif
|
||||
|
||||
#ifndef kh_inline
|
||||
#ifdef _MSC_VER
|
||||
#define kh_inline __inline
|
||||
#else
|
||||
#define kh_inline inline
|
||||
#endif
|
||||
#endif /* kh_inline */
|
||||
|
||||
#ifndef klib_unused
|
||||
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
|
||||
#define klib_unused __attribute__ ((__unused__))
|
||||
#else
|
||||
#define klib_unused
|
||||
#endif
|
||||
#endif /* klib_unused */
|
||||
|
||||
typedef khint32_t khint_t;
|
||||
typedef khint_t khiter_t;
|
||||
|
||||
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||
|
||||
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||
|
||||
#ifndef kroundup32
|
||||
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
#endif
|
||||
|
||||
#ifndef kcalloc
|
||||
#define kcalloc(N,Z) calloc(N,Z)
|
||||
#endif
|
||||
#ifndef kmalloc
|
||||
#define kmalloc(Z) malloc(Z)
|
||||
#endif
|
||||
#ifndef krealloc
|
||||
#define krealloc(P,Z) realloc(P,Z)
|
||||
#endif
|
||||
#ifndef kfree
|
||||
#define kfree(P) free(P)
|
||||
#endif
|
||||
|
||||
static const double __ac_HASH_UPPER = 0.77;
|
||||
|
||||
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
typedef struct kh_##name##_s { \
|
||||
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||
khint32_t *flags; \
|
||||
khkey_t *keys; \
|
||||
khval_t *vals; \
|
||||
} kh_##name##_t;
|
||||
|
||||
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||
extern kh_##name##_t *kh_init_##name(void); \
|
||||
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||
|
||||
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||
} \
|
||||
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h) { \
|
||||
kfree((void *)h->keys); kfree(h->flags); \
|
||||
kfree((void *)h->vals); \
|
||||
kfree(h); \
|
||||
} \
|
||||
} \
|
||||
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h && h->flags) { \
|
||||
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||
h->size = h->n_occupied = 0; \
|
||||
} \
|
||||
} \
|
||||
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||
{ \
|
||||
if (h->n_buckets) { \
|
||||
khint_t k, i, last, mask, step = 0; \
|
||||
mask = h->n_buckets - 1; \
|
||||
k = __hash_func(key); i = k & mask; \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) return h->n_buckets; \
|
||||
} \
|
||||
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||
} else return 0; \
|
||||
} \
|
||||
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||
khint32_t *new_flags = 0; \
|
||||
khint_t j = 1; \
|
||||
{ \
|
||||
kroundup32(new_n_buckets); \
|
||||
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (!new_flags) return -1; \
|
||||
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (!new_keys) { kfree(new_flags); return -1; } \
|
||||
h->keys = new_keys; \
|
||||
if (kh_is_map) { \
|
||||
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
if (!new_vals) { kfree(new_flags); return -1; } \
|
||||
h->vals = new_vals; \
|
||||
} \
|
||||
} /* otherwise shrink */ \
|
||||
} \
|
||||
} \
|
||||
if (j) { /* rehashing is needed */ \
|
||||
for (j = 0; j != h->n_buckets; ++j) { \
|
||||
if (__ac_iseither(h->flags, j) == 0) { \
|
||||
khkey_t key = h->keys[j]; \
|
||||
khval_t val; \
|
||||
khint_t new_mask; \
|
||||
new_mask = new_n_buckets - 1; \
|
||||
if (kh_is_map) val = h->vals[j]; \
|
||||
__ac_set_isdel_true(h->flags, j); \
|
||||
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||
khint_t k, i, step = 0; \
|
||||
k = __hash_func(key); \
|
||||
i = k & new_mask; \
|
||||
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
|
||||
__ac_set_isempty_false(new_flags, i); \
|
||||
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||
} else { /* write the element and jump out of the loop */ \
|
||||
h->keys[i] = key; \
|
||||
if (kh_is_map) h->vals[i] = val; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
} \
|
||||
kfree(h->flags); /* free the working space */ \
|
||||
h->flags = new_flags; \
|
||||
h->n_buckets = new_n_buckets; \
|
||||
h->n_occupied = h->size; \
|
||||
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||
{ \
|
||||
khint_t x; \
|
||||
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||
if (h->n_buckets > (h->size<<1)) { \
|
||||
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||
{ \
|
||||
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
|
||||
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||
else { \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
if (__ac_isdel(h->flags, i)) site = i; \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) { x = site; break; } \
|
||||
} \
|
||||
if (x == h->n_buckets) { \
|
||||
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||
else x = i; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; ++h->n_occupied; \
|
||||
*ret = 1; \
|
||||
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; \
|
||||
*ret = 2; \
|
||||
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||
return x; \
|
||||
} \
|
||||
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||
{ \
|
||||
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||
__ac_set_isdel_true(h->flags, x); \
|
||||
--h->size; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||
|
||||
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||
|
||||
/*! @function
|
||||
@abstract Integer hash function
|
||||
@param key The integer [khint32_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||
/*! @function
|
||||
@abstract Integer comparison function
|
||||
*/
|
||||
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract 64-bit integer hash function
|
||||
@param key The integer [khint64_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||
/*! @function
|
||||
@abstract 64-bit integer comparison function
|
||||
*/
|
||||
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract const char* hash function
|
||||
@param s Pointer to a null terminated string
|
||||
@return The hash value
|
||||
*/
|
||||
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||
{
|
||||
khint_t h = (khint_t)*s;
|
||||
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||
return h;
|
||||
}
|
||||
/*! @function
|
||||
@abstract Another interface to const char* hash function
|
||||
@param key Pointer to a null terminated string [const char*]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||
/*! @function
|
||||
@abstract Const char* comparison function
|
||||
*/
|
||||
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||
|
||||
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||
{
|
||||
key += ~(key << 15);
|
||||
key ^= (key >> 10);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 6);
|
||||
key += ~(key << 11);
|
||||
key ^= (key >> 16);
|
||||
return key;
|
||||
}
|
||||
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
|
||||
|
||||
/* --- END OF HASH FUNCTIONS --- */
|
||||
|
||||
/* Other convenient macros... */
|
||||
|
||||
/*!
|
||||
@abstract Type of the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define khash_t(name) kh_##name##_t
|
||||
|
||||
/*! @function
|
||||
@abstract Initiate a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@return Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_init(name) kh_init_##name()
|
||||
|
||||
/*! @function
|
||||
@abstract Destroy a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Reset a hash table without deallocating memory.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_clear(name, h) kh_clear_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Resize a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param s New size [khint_t]
|
||||
*/
|
||||
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||
|
||||
/*! @function
|
||||
@abstract Insert a key to the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@param r Extra return code: -1 if the operation failed;
|
||||
0 if the key is present in the hash table;
|
||||
1 if the bucket is empty (never used); 2 if the element in
|
||||
the bucket has been deleted [int*]
|
||||
@return Iterator to the inserted element [khint_t]
|
||||
*/
|
||||
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||
|
||||
/*! @function
|
||||
@abstract Retrieve a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||
*/
|
||||
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Remove a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Iterator to the element to be deleted [khint_t]
|
||||
*/
|
||||
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Test whether a bucket contains data.
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return 1 if containing data; 0 otherwise [int]
|
||||
*/
|
||||
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||
|
||||
/*! @function
|
||||
@abstract Get key given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Key [type of keys]
|
||||
*/
|
||||
#define kh_key(h, x) ((h)->keys[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get value given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Value [type of values]
|
||||
@discussion For hash sets, calling this results in segfault.
|
||||
*/
|
||||
#define kh_val(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Alias of kh_val()
|
||||
*/
|
||||
#define kh_value(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get the start iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The start iterator [khint_t]
|
||||
*/
|
||||
#define kh_begin(h) (khint_t)(0)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the end iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The end iterator [khint_t]
|
||||
*/
|
||||
#define kh_end(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of elements in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of elements in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_size(h) ((h)->size)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of buckets in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of buckets in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the entries in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param kvar Variable to which key will be assigned
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(kvar) = kh_key(h,__i); \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the values in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/* More convenient interfaces */
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT(name) \
|
||||
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT64(name) \
|
||||
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
typedef const char *kh_cstr_t;
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_STR(name) \
|
||||
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
#endif /* __AC_KHASH_H */
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
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 "fmp.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
typedef struct lfmp_t lfmp_t;
|
||||
struct lfmp_t
|
||||
{
|
||||
fmp_t fmp;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_init(lfmp_t *lfmp_,
|
||||
const uint64_t obj_size_,
|
||||
const uint64_t page_multiple_)
|
||||
{
|
||||
fmp_init(&lfmp_->fmp,obj_size_,page_multiple_);
|
||||
pthread_mutex_init(&lfmp_->lock,NULL);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_lock(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_unlock(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_slab_count(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_slab_count(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lfmp_slab_alloc(lfmp_t *lfmp_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_slab_alloc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
lfmp_alloc(lfmp_t *lfmp_)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_alloc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
lfmp_calloc(lfmp_t *lfmp_)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_calloc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_free(lfmp_t *lfmp_,
|
||||
void *obj_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_free(&lfmp_->fmp,obj_);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_clear(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_clear(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_destroy(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_destroy(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
pthread_mutex_destroy(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_avail_objs(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_avail_objs(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_objs_in_slab(lfmp_t *lfmp_,
|
||||
void *slab_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_objs_in_slab(&lfmp_->fmp,slab_);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_remove_objs_in_slab(lfmp_t *lfmp_,
|
||||
void *slab_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_remove_objs_in_slab(&lfmp_->fmp,slab_);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lfmp_gc(lfmp_t *lfmp_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_gc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_objs_per_slab(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_objs_per_slab(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
double
|
||||
lfmp_slab_usage_ratio(lfmp_t *lfmp_)
|
||||
{
|
||||
double rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_slab_usage_ratio(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_total_allocated_memory(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_total_allocated_memory(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -28,7 +28,6 @@
|
||||
#define FUSE_DEV_TRUNK "/dev/fuse"
|
||||
|
||||
enum {
|
||||
KEY_ALLOW_ROOT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
@@ -37,7 +36,6 @@ enum {
|
||||
|
||||
struct mount_opts {
|
||||
int allow_other;
|
||||
int allow_root;
|
||||
int ishelp;
|
||||
char *kernel_opts;
|
||||
};
|
||||
@@ -47,8 +45,6 @@ struct mount_opts {
|
||||
|
||||
static const struct fuse_opt fuse_mount_opts[] = {
|
||||
{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
|
||||
{ "allow_root", offsetof(struct mount_opts, allow_root), 1 },
|
||||
FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
|
||||
FUSE_OPT_KEY("-r", KEY_RO),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
@@ -101,9 +97,6 @@ static const struct fuse_opt fuse_mount_opts[] = {
|
||||
|
||||
static void mount_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o allow_root allow access to root\n"
|
||||
);
|
||||
system(FUSERMOUNT_PROG " --help");
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
@@ -119,12 +112,6 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
||||
struct mount_opts *mo = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_ALLOW_ROOT:
|
||||
if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
|
||||
fuse_opt_add_arg(outargs, "-oallow_root") == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
@@ -328,10 +315,6 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
|
||||
if (mo.allow_other && mo.allow_root) {
|
||||
fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
|
||||
goto out;
|
||||
}
|
||||
if (mo.ishelp)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@ enum {
|
||||
KEY_FUSERMOUNT_OPT,
|
||||
KEY_SUBTYPE_OPT,
|
||||
KEY_MTAB_OPT,
|
||||
KEY_ALLOW_ROOT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
@@ -63,7 +62,6 @@ enum {
|
||||
|
||||
struct mount_opts {
|
||||
int allow_other;
|
||||
int allow_root;
|
||||
int ishelp;
|
||||
int flags;
|
||||
int nonempty;
|
||||
@@ -81,14 +79,12 @@ struct mount_opts {
|
||||
|
||||
static const struct fuse_opt fuse_mount_opts[] = {
|
||||
FUSE_MOUNT_OPT("allow_other", allow_other),
|
||||
FUSE_MOUNT_OPT("allow_root", allow_root),
|
||||
FUSE_MOUNT_OPT("nonempty", nonempty),
|
||||
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("allow_root", KEY_ALLOW_ROOT),
|
||||
FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
|
||||
FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
|
||||
@@ -129,7 +125,6 @@ static void mount_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o allow_other allow access to other users\n"
|
||||
" -o allow_root allow access to root\n"
|
||||
" -o auto_unmount auto unmount on process termination\n"
|
||||
" -o nonempty allow mounts over non-empty file/dir\n"
|
||||
" -o default_permissions enable permission checking by kernel\n"
|
||||
@@ -208,12 +203,6 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
||||
struct mount_opts *mo = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_ALLOW_ROOT:
|
||||
if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
|
||||
fuse_opt_add_arg(outargs, "-oallow_root") == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
@@ -575,10 +564,6 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
|
||||
if (mo.allow_other && mo.allow_root) {
|
||||
fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
|
||||
goto out;
|
||||
}
|
||||
res = 0;
|
||||
if (mo.ishelp)
|
||||
goto out;
|
||||
|
||||
@@ -782,9 +782,7 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
|
||||
skip_option = 1;
|
||||
}
|
||||
}
|
||||
if (getuid() != 0 && !user_allow_other &&
|
||||
(opt_eq(s, len, "allow_other") ||
|
||||
opt_eq(s, len, "allow_root"))) {
|
||||
if (getuid() != 0 && !user_allow_other && opt_eq(s, len, "allow_other")) {
|
||||
fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
|
||||
goto err;
|
||||
}
|
||||
|
||||
+439
-204
@@ -1,7 +1,7 @@
|
||||
.\"t
|
||||
.\" Automatically generated by Pandoc 1.19.2.4
|
||||
.\"
|
||||
.TH "mergerfs" "1" "2020\-08\-30" "mergerfs user manual" ""
|
||||
.TH "mergerfs" "1" "2021\-05\-29" "mergerfs user manual" ""
|
||||
.hy
|
||||
.SH NAME
|
||||
.PP
|
||||
@@ -17,32 +17,31 @@ devices.
|
||||
It is similar to \f[B]mhddfs\f[], \f[B]unionfs\f[], and \f[B]aufs\f[].
|
||||
.SH FEATURES
|
||||
.IP \[bu] 2
|
||||
Runs in userspace (FUSE)
|
||||
.IP \[bu] 2
|
||||
Configurable behaviors / file placement
|
||||
.IP \[bu] 2
|
||||
Ability to add or remove filesystems at will
|
||||
.IP \[bu] 2
|
||||
Resistance to individual filesystem failure
|
||||
.IP \[bu] 2
|
||||
Support for extended attributes (xattrs)
|
||||
.IP \[bu] 2
|
||||
Support for file attributes (chattr)
|
||||
.IP \[bu] 2
|
||||
Runtime configurable (via xattrs)
|
||||
.IP \[bu] 2
|
||||
Safe to run as root
|
||||
.IP \[bu] 2
|
||||
Opportunistic credential caching
|
||||
.IP \[bu] 2
|
||||
Works with heterogeneous filesystem types
|
||||
.IP \[bu] 2
|
||||
Handling of writes to full drives (transparently move file to drive with
|
||||
capacity)
|
||||
Moving of file when filesystem runs out of space while writing
|
||||
.IP \[bu] 2
|
||||
Handles pool of read\-only and read/write drives
|
||||
Ignore read\-only filesystems when creating files
|
||||
.IP \[bu] 2
|
||||
Can turn read\-only files into symlinks to underlying file
|
||||
Turn read\-only files into symlinks to underlying file
|
||||
.IP \[bu] 2
|
||||
Hard link copy\-on\-write / CoW
|
||||
.IP \[bu] 2
|
||||
supports POSIX ACLs
|
||||
Support for POSIX ACLs
|
||||
.IP \[bu] 2
|
||||
Misc other things
|
||||
.SH HOW IT WORKS
|
||||
.PP
|
||||
mergerfs logically merges multiple paths together.
|
||||
@@ -74,14 +73,14 @@ A\ \ \ \ \ \ \ \ \ +\ \ \ \ \ \ B\ \ \ \ \ \ \ \ =\ \ \ \ \ \ \ C
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
mergerfs does \f[B]not\f[] support the copy\-on\-write (CoW) behavior
|
||||
found in \f[B]aufs\f[] and \f[B]overlayfs\f[].
|
||||
mergerfs does \f[B]NOT\f[] support the copy\-on\-write (CoW) or whiteout
|
||||
behaviors found in \f[B]aufs\f[] and \f[B]overlayfs\f[].
|
||||
You can \f[B]not\f[] mount a read\-only filesystem and write to it.
|
||||
However, mergerfs will ignore read\-only drives when creating new files
|
||||
so you can mix read\-write and read\-only drives.
|
||||
It also does \f[B]not\f[] split data across drives.
|
||||
It also does \f[B]NOT\f[] split data across drives.
|
||||
It is not RAID0 / striping.
|
||||
It is simply a union.
|
||||
It is simply a union of other filesystems.
|
||||
.SH TERMINOLOGY
|
||||
.IP \[bu] 2
|
||||
branch: A base path used in the pool.
|
||||
@@ -91,33 +90,39 @@ The union of the branches.
|
||||
.IP \[bu] 2
|
||||
relative path: The path in the pool relative to the branch and mount.
|
||||
.IP \[bu] 2
|
||||
function: A filesystem call (open, unlink, create, getattr, rmdir, etc.)
|
||||
.IP \[bu] 2
|
||||
category: A collection of functions based on basic behavior (action,
|
||||
create, search).
|
||||
.IP \[bu] 2
|
||||
policy: The algorithm used to select a file when performing a function.
|
||||
.IP \[bu] 2
|
||||
function: A filesystem call (open, unlink, create, getattr, etc.)
|
||||
.IP \[bu] 2
|
||||
category: A collection of functions (action, create, search).
|
||||
.IP \[bu] 2
|
||||
path preservation: Aspect of some policies which includes checking the
|
||||
path for which a file would be created.
|
||||
.SH BASIC SETUP
|
||||
.PP
|
||||
If you don\[aq]t already know that you have a special use case then just
|
||||
start with one of the following option sets.
|
||||
.SS You need \f[C]mmap\f[] (used by rtorrent and many sqlite3 base
|
||||
software)
|
||||
.PP
|
||||
\f[C]allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs\f[]
|
||||
.SS You don\[aq]t need \f[C]mmap\f[]
|
||||
.PP
|
||||
\f[C]use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs\f[]
|
||||
.SS You do need \f[C]mmap\f[] (used by rtorrent and some other programs)
|
||||
.PP
|
||||
\f[C]use_ino,cache.files=partial,dropcacheonclose=true,allow_other,category.create=mfs\f[]
|
||||
\f[C]allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs\f[]
|
||||
.PP
|
||||
See the mergerfs wiki for real world
|
||||
deployments (https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments)
|
||||
for comparisons / ideas.
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
These options are the same regardless you use them with the
|
||||
\f[C]mergerfs\f[] commandline program, used in fstab, or in a config
|
||||
file.
|
||||
.SS mount options
|
||||
.IP \[bu] 2
|
||||
\f[B]config\f[]: Path to a config file.
|
||||
Same arguments as below in key=val format.
|
||||
Same arguments as below in key=val / ini style format.
|
||||
.IP \[bu] 2
|
||||
\f[B]branches\f[]: Colon delimited list of branches.
|
||||
.IP \[bu] 2
|
||||
@@ -127,6 +132,7 @@ This is required for most use\-cases.
|
||||
.IP \[bu] 2
|
||||
\f[B]minfreespace=SIZE\f[]: The minimum space value used for creation
|
||||
policies.
|
||||
Can be overridden by branch specific option.
|
||||
Understands \[aq]K\[aq], \[aq]M\[aq], and \[aq]G\[aq] to represent
|
||||
kilobyte, megabyte, and gigabyte respectively.
|
||||
(default: 4G)
|
||||
@@ -162,7 +168,7 @@ be reported as symlinks to the original files.
|
||||
Please read more below before using.
|
||||
(default: false)
|
||||
.IP \[bu] 2
|
||||
\f[B]symlinkify_timeout=INT\f[]: Time to wait, in seconds, to activate
|
||||
\f[B]symlinkify_timeout=UINT\f[]: Time to wait, in seconds, to activate
|
||||
the \f[B]symlinkify\f[] behavior.
|
||||
(default: 3600)
|
||||
.IP \[bu] 2
|
||||
@@ -216,6 +222,18 @@ over NFS where there are issues with creating files for write while
|
||||
setting the mode to read\-only.
|
||||
(default: off)
|
||||
.IP \[bu] 2
|
||||
\f[B]follow\-symlinks=never|directory|regular|all\f[]: Turns symlinks
|
||||
into what they point to.
|
||||
(default: never)
|
||||
.IP \[bu] 2
|
||||
\f[B]link\-exdev=passthrough|rel\-symlink|abs\-base\-symlink|abs\-pool\-symlink\f[]:
|
||||
When a link fails with EXDEV optionally create a symlink to the file
|
||||
instead.
|
||||
.IP \[bu] 2
|
||||
\f[B]rename\-exdev=passthrough|rel\-symlink|abs\-symlink\f[]: When a
|
||||
rename fails with EXDEV optionally move the file to a special directory
|
||||
and symlink to it.
|
||||
.IP \[bu] 2
|
||||
\f[B]posix_acl=BOOL\f[]: Enable POSIX ACL support (if supported by
|
||||
kernel and underlying filesystem).
|
||||
(default: false)
|
||||
@@ -226,7 +244,7 @@ pending read request per file handle and will attempt to order requests
|
||||
by offset.
|
||||
(default: true)
|
||||
.IP \[bu] 2
|
||||
\f[B]fuse_msg_size=INT\f[]: Set the max number of pages per FUSE
|
||||
\f[B]fuse_msg_size=UINT\f[]: Set the max number of pages per FUSE
|
||||
message.
|
||||
Only available on Linux >= 4.20 and ignored otherwise.
|
||||
(min: 1; max: 256; default: 256)
|
||||
@@ -253,24 +271,32 @@ longest common prefix removed.
|
||||
See below for the list of value types.
|
||||
Example: \f[B]func.getattr=newest\f[]
|
||||
.IP \[bu] 2
|
||||
\f[B]category.CATEGORY=POLICY\f[]: Sets policy of all FUSE functions in
|
||||
the provided category.
|
||||
See POLICIES section for defaults.
|
||||
Example: \f[B]category.create=mfs\f[]
|
||||
\f[B]category.action=POLICY\f[]: Sets policy of all FUSE functions in
|
||||
the action category.
|
||||
(default: epall)
|
||||
.IP \[bu] 2
|
||||
\f[B]cache.open=INT\f[]: \[aq]open\[aq] policy cache timeout in seconds.
|
||||
\f[B]category.create=POLICY\f[]: Sets policy of all FUSE functions in
|
||||
the create category.
|
||||
(default: epmfs)
|
||||
.IP \[bu] 2
|
||||
\f[B]category.search=POLICY\f[]: Sets policy of all FUSE functions in
|
||||
the search category.
|
||||
(default: ff)
|
||||
.IP \[bu] 2
|
||||
\f[B]cache.open=UINT\f[]: \[aq]open\[aq] policy cache timeout in
|
||||
seconds.
|
||||
(default: 0)
|
||||
.IP \[bu] 2
|
||||
\f[B]cache.statfs=INT\f[]: \[aq]statfs\[aq] cache timeout in seconds.
|
||||
\f[B]cache.statfs=UINT\f[]: \[aq]statfs\[aq] cache timeout in seconds.
|
||||
(default: 0)
|
||||
.IP \[bu] 2
|
||||
\f[B]cache.attr=INT\f[]: File attribute cache timeout in seconds.
|
||||
\f[B]cache.attr=UINT\f[]: File attribute cache timeout in seconds.
|
||||
(default: 1)
|
||||
.IP \[bu] 2
|
||||
\f[B]cache.entry=INT\f[]: File name lookup cache timeout in seconds.
|
||||
\f[B]cache.entry=UINT\f[]: File name lookup cache timeout in seconds.
|
||||
(default: 1)
|
||||
.IP \[bu] 2
|
||||
\f[B]cache.negative_entry=INT\f[]: Negative file name lookup cache
|
||||
\f[B]cache.negative_entry=UINT\f[]: Negative file name lookup cache
|
||||
timeout in seconds.
|
||||
(default: 0)
|
||||
.IP \[bu] 2
|
||||
@@ -314,45 +340,59 @@ setting.
|
||||
.IP \[bu] 2
|
||||
BOOL = \[aq]true\[aq] | \[aq]false\[aq]
|
||||
.IP \[bu] 2
|
||||
INT = [0,MAX_INT]
|
||||
INT = [MIN_INT,MAX_INT]
|
||||
.IP \[bu] 2
|
||||
UINT = [0,MAX_INT]
|
||||
.IP \[bu] 2
|
||||
SIZE = \[aq]NNM\[aq]; NN = INT, M = \[aq]K\[aq] | \[aq]M\[aq] |
|
||||
\[aq]G\[aq] | \[aq]T\[aq]
|
||||
.IP \[bu] 2
|
||||
STR = string
|
||||
.IP \[bu] 2
|
||||
FUNC = FUSE function
|
||||
FUNC = filesystem function
|
||||
.IP \[bu] 2
|
||||
CATEGORY = FUSE function category
|
||||
CATEGORY = function category
|
||||
.IP \[bu] 2
|
||||
POLICY = mergerfs function policy
|
||||
.SS branches
|
||||
.PP
|
||||
The \[aq]branches\[aq] (formerly \[aq]srcmounts\[aq]) argument is a
|
||||
colon (\[aq]:\[aq]) delimited list of paths to be pooled together.
|
||||
The \[aq]branches\[aq] argument is a colon (\[aq]:\[aq]) delimited list
|
||||
of paths to be pooled together.
|
||||
It does not matter if the paths are on the same or different drives nor
|
||||
does it matter the filesystem.
|
||||
does it matter the filesystem (within reason).
|
||||
Used and available space will not be duplicated for paths on the same
|
||||
device and any features which aren\[aq]t supported by the underlying
|
||||
filesystem (such as file attributes or extended attributes) will return
|
||||
the appropriate errors.
|
||||
.PP
|
||||
Branches currently have two options which can be set.
|
||||
A type which impacts whether or not the branch is included in a policy
|
||||
calculation and a individual minfreespace value.
|
||||
The values are set by prepending an \f[C]=\f[] at the end of a branch
|
||||
designation and using commas as delimiters.
|
||||
Example: /mnt/drive=RW,1234
|
||||
.SS branch type
|
||||
.IP \[bu] 2
|
||||
RW: (read/write) \- Default behavior.
|
||||
Will be eligible in all policy categories.
|
||||
.IP \[bu] 2
|
||||
RO: (read\-only) \- Will be excluded from \f[C]create\f[] and
|
||||
\f[C]action\f[] policies.
|
||||
Same as a read\-only mounted filesystem would be (though faster to
|
||||
process).
|
||||
.IP \[bu] 2
|
||||
NC: (no\-create) \- Will be excluded from \f[C]create\f[] policies.
|
||||
You can\[aq]t create on that branch but you can change or delete.
|
||||
.SS minfreespace
|
||||
.PP
|
||||
Same purpose as the global option but specific to the branch.
|
||||
If not set the global value is used.
|
||||
.SS globbing
|
||||
.PP
|
||||
To make it easier to include multiple branches mergerfs supports
|
||||
globbing (http://linux.die.net/man/7/glob).
|
||||
\f[B]The globbing tokens MUST be escaped when using via the shell else
|
||||
the shell itself will apply the glob itself.\f[]
|
||||
.PP
|
||||
Each branch can have a suffix of \f[C]=RW\f[] (read / write),
|
||||
\f[C]=RO\f[] (read\-only), or \f[C]=NC\f[] (no create).
|
||||
These suffixes work with globs as well and will apply to each path
|
||||
found.
|
||||
\f[C]RW\f[] is the default behavior and those paths will be eligible for
|
||||
all policy categories.
|
||||
\f[C]RO\f[] will exclude those paths from \f[C]create\f[] and
|
||||
\f[C]action\f[] policies (just as a filesystem being mounted \f[C]ro\f[]
|
||||
would).
|
||||
\f[C]NC\f[] will exclude those paths from \f[C]create\f[] policies (you
|
||||
can\[aq]t create but you can change / delete).
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
@@ -373,8 +413,8 @@ tools use \f[B]/etc/fstab\f[].
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
\f[B]NOTE:\f[] the globbing is done at mount or xattr update time (see
|
||||
below).
|
||||
\f[B]NOTE:\f[] the globbing is done at mount or when updated using the
|
||||
runtime API.
|
||||
If a new directory is added matching the glob after the fact it will not
|
||||
be automatically included.
|
||||
.PP
|
||||
@@ -513,6 +553,75 @@ On kernels before 4.20 the value has no effect.
|
||||
The reason the value is configurable is to enable experimentation and
|
||||
benchmarking.
|
||||
See the BENCHMARKING section for examples.
|
||||
.SS follow\-symlinks
|
||||
.PP
|
||||
This feature, when enabled, will cause symlinks to be interpreted by
|
||||
mergerfs as their target (depending on the mode).
|
||||
.PP
|
||||
When there is a getattr/stat request for a file mergerfs will check if
|
||||
the file is a symlink and depending on the \f[C]follow\-symlinks\f[]
|
||||
setting will replace the information about the symlink with that of that
|
||||
which it points to.
|
||||
.PP
|
||||
When unlink\[aq]ing or rmdir\[aq]ing the followed symlink it will remove
|
||||
the symlink itself and not that which it points to.
|
||||
.IP \[bu] 2
|
||||
never: Behave as normal.
|
||||
Symlinks are treated as such.
|
||||
.IP \[bu] 2
|
||||
directory: Resolve symlinks only which point to directories.
|
||||
.IP \[bu] 2
|
||||
regular: Resolve symlinks only which point to regular files.
|
||||
.IP \[bu] 2
|
||||
all: Resolve all symlinks to that which they point to.
|
||||
.PP
|
||||
Symlinks which do not point to anything are left as is.
|
||||
.PP
|
||||
WARNING: This feature works but there might be edge cases yet found.
|
||||
If you find any odd behaviors please file a ticket on
|
||||
github (https://github.com/trapexit/mergerfs/issues).
|
||||
.SS link\-exdev
|
||||
.PP
|
||||
If using path preservation and a \f[C]link\f[] fails with EXDEV make a
|
||||
call to \f[C]symlink\f[] where the \f[C]target\f[] is the
|
||||
\f[C]oldlink\f[] and the \f[C]linkpath\f[] is the \f[C]newpath\f[].
|
||||
The \f[C]target\f[] value is determined by the value of
|
||||
\f[C]link\-exdev\f[].
|
||||
.IP \[bu] 2
|
||||
passthrough: Return EXDEV as normal.
|
||||
.IP \[bu] 2
|
||||
rel\-symlink: A relative path from the \f[C]newpath\f[].
|
||||
.IP \[bu] 2
|
||||
abs\-base\-symlink: A absolute value using the underlying branch.
|
||||
.IP \[bu] 2
|
||||
abs\-pool\-symlink: A absolute value using the mergerfs mount point.
|
||||
.PP
|
||||
NOTE: It is possible that some applications check the file they link.
|
||||
In those cases it is possible it will error or complain.
|
||||
.SS rename\-exdev
|
||||
.PP
|
||||
If using path preservation and a \f[C]rename\f[] fails with EXDEV:
|
||||
.IP "1." 3
|
||||
Move file from \f[B]/branch/a/b/c\f[] to
|
||||
\f[B]/branch/.mergerfs_rename_exdev/a/b/c\f[].
|
||||
.IP "2." 3
|
||||
symlink the rename\[aq]s \f[C]newpath\f[] to the moved file.
|
||||
.PP
|
||||
The \f[C]target\f[] value is determined by the value of
|
||||
\f[C]rename\-exdev\f[].
|
||||
.IP \[bu] 2
|
||||
passthrough: Return EXDEV as normal.
|
||||
.IP \[bu] 2
|
||||
rel\-symlink: A relative path from the \f[C]newpath\f[].
|
||||
.IP \[bu] 2
|
||||
abs\-symlink: A absolute value using the mergerfs mount point.
|
||||
.PP
|
||||
NOTE: It is possible that some applications check the file they rename.
|
||||
In those cases it is possible it will error or complain.
|
||||
.PP
|
||||
NOTE: The reason \f[C]abs\-symlink\f[] is not split into two like
|
||||
\f[C]link\-exdev\f[] is due to the complexities in managing absolute
|
||||
base symlinks when multiple \f[C]oldpaths\f[] exist.
|
||||
.SS symlinkify
|
||||
.PP
|
||||
Due to the levels of indirection introduced by mergerfs and the
|
||||
@@ -600,7 +709,7 @@ If set to \f[C]git\f[] it will only perform the hack when the path in
|
||||
question includes \f[C]/.git/\f[].
|
||||
\f[C]all\f[] will result it it applying anytime a readonly file which is
|
||||
empty is opened for writing.
|
||||
.SH FUNCTIONS / POLICIES / CATEGORIES
|
||||
.SH FUNCTIONS, CATEGORIES and POLICIES
|
||||
.PP
|
||||
The POSIX filesystem API is made up of a number of functions.
|
||||
\f[B]creat\f[], \f[B]stat\f[], \f[B]chown\f[], etc.
|
||||
@@ -608,34 +717,34 @@ For ease of configuration in mergerfs most of the core functions are
|
||||
grouped into 3 categories: \f[B]action\f[], \f[B]create\f[], and
|
||||
\f[B]search\f[].
|
||||
These functions and categories can be assigned a policy which dictates
|
||||
which underlying branch/file/directory is chosen when performing that
|
||||
behavior.
|
||||
Any policy can be assigned to a function or category though some may not
|
||||
be very useful in practice.
|
||||
For instance: \f[B]rand\f[] (random) may be useful for file creation
|
||||
(create) but could lead to very odd behavior if used for \f[C]chmod\f[]
|
||||
if there were more than one copy of the file.
|
||||
which branch is chosen when performing that function.
|
||||
.PP
|
||||
Some functions, listed in the category \f[C]N/A\f[] below, can not be
|
||||
assigned the normal policies.
|
||||
All functions which work on file handles use the handle which was
|
||||
acquired by \f[C]open\f[] or \f[C]create\f[].
|
||||
\f[C]readdir\f[] has no real need for a policy given the purpose is
|
||||
merely to return a list of entries in a directory.
|
||||
\f[C]statfs\f[]\[aq]s behavior can be modified via other options.
|
||||
These functions work with file handles, rather than file paths, which
|
||||
were created by \f[C]open\f[] or \f[C]create\f[].
|
||||
That said many times the current FUSE kernel driver will not always
|
||||
provide the file handle when a client calls \f[C]fgetattr\f[],
|
||||
\f[C]fchown\f[], \f[C]fchmod\f[], \f[C]futimens\f[], \f[C]ftruncate\f[],
|
||||
etc.
|
||||
This means it will call the regular, path based, versions.
|
||||
\f[C]readdir\f[] has no real need for a policy given the purpose is
|
||||
merely to return a list of entries in a directory.
|
||||
\f[C]statfs\f[]\[aq]s behavior can be modified via other options.
|
||||
.PP
|
||||
When using policies which are based on a branch\[aq]s available space
|
||||
the base path provided is used.
|
||||
Not the full path to the file in question.
|
||||
Meaning that sub mounts won\[aq]t be considered in the space
|
||||
Meaning that mounts in the branch won\[aq]t be considered in the space
|
||||
calculations.
|
||||
The reason is that it doesn\[aq]t really work for non\-path preserving
|
||||
policies and can lead to non\-obvious behaviors.
|
||||
.PP
|
||||
NOTE: While any policy can be assigned to a function or category though
|
||||
some may not be very useful in practice.
|
||||
For instance: \f[B]rand\f[] (random) may be useful for file creation
|
||||
(create) but could lead to very odd behavior if used for \f[C]chmod\f[]
|
||||
if there were more than one copy of the file.
|
||||
.SS Functions and their Category classifications
|
||||
.PP
|
||||
.TS
|
||||
@@ -672,8 +781,44 @@ fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl
|
||||
T}
|
||||
.TE
|
||||
.PP
|
||||
In cases where something may be searched (to confirm a directory exists
|
||||
across all source mounts) \f[B]getattr\f[] will be used.
|
||||
In cases where something may be searched for (such as a path to clone)
|
||||
\f[B]getattr\f[] will usually be used.
|
||||
.SS Policies
|
||||
.PP
|
||||
A policy is the algorithm used to choose a branch or branches for a
|
||||
function to work on.
|
||||
Think of them as ways to filter and sort branches.
|
||||
.PP
|
||||
Any function in the \f[C]create\f[] category will clone the relative
|
||||
path if needed.
|
||||
Some other functions (\f[C]rename\f[],\f[C]link\f[],\f[C]ioctl\f[]) have
|
||||
special requirements or behaviors which you can read more about below.
|
||||
.SS Filtering
|
||||
.PP
|
||||
Policies basically search branches and create a list of files / paths
|
||||
for functions to work on.
|
||||
The policy is responsible for filtering and sorting the branches.
|
||||
Filters include \f[B]minfreespace\f[], whether or not a branch is
|
||||
mounted read\-only, and the branch tagging (RO,NC,RW).
|
||||
These filters are applied across all policies unless otherwise noted.
|
||||
.IP \[bu] 2
|
||||
No \f[B]search\f[] function policies filter.
|
||||
.IP \[bu] 2
|
||||
All \f[B]action\f[] function policies filter out branches which are
|
||||
mounted \f[B]read\-only\f[] or tagged as \f[B]RO (read\-only)\f[].
|
||||
.IP \[bu] 2
|
||||
All \f[B]create\f[] function policies filter out branches which are
|
||||
mounted \f[B]read\-only\f[], tagged \f[B]RO (read\-only)\f[] or \f[B]NC
|
||||
(no create)\f[], or has available space less than \f[C]minfreespace\f[].
|
||||
.PP
|
||||
Policies may have their own additional filtering such as those that
|
||||
require existing paths to be present.
|
||||
.PP
|
||||
If all branches are filtered an error will be returned.
|
||||
Typically \f[B]EROFS\f[] (read\-only filesystem) or \f[B]ENOSPC\f[] (no
|
||||
space left on device) depending on the most recent reason for filtering
|
||||
a branch.
|
||||
\f[B]ENOENT\f[] will be returned if no elegible branch is found.
|
||||
.SS Path Preservation
|
||||
.PP
|
||||
Policies, as described below, are of two basic types.
|
||||
@@ -696,33 +841,14 @@ defined as \f[C]path\ preserving\f[] for the purpose of controlling
|
||||
\f[C]ignorepponrename\f[] is available to disable that behavior.
|
||||
In mergerfs v3.0 the path preserving behavior of rename and link will
|
||||
likely be separated from the policy all together.
|
||||
.SS Filters
|
||||
.PP
|
||||
Policies basically search branches and create a list of files / paths
|
||||
for functions to work on.
|
||||
The policy is responsible for filtering and sorting.
|
||||
Filters include \f[B]minfreespace\f[], whether or not a branch is
|
||||
mounted read\-only, and the branch tagging (RO,NC,RW).
|
||||
The policy defines the sorting but filtering is mostly uniform as
|
||||
described below.
|
||||
.IP \[bu] 2
|
||||
No \f[B]search\f[] policies filter.
|
||||
.IP \[bu] 2
|
||||
All \f[B]action\f[] policies will filter out branches which are mounted
|
||||
\f[B]read\-only\f[] or tagged as \f[B]RO (read\-only)\f[].
|
||||
.IP \[bu] 2
|
||||
All \f[B]create\f[] policies will filter out branches which are mounted
|
||||
\f[B]read\-only\f[], tagged \f[B]RO (read\-only)\f[] or \f[B]NC (no
|
||||
create)\f[], or has available space less than \f[C]minfreespace\f[].
|
||||
.PP
|
||||
If all branches are filtered an error will be returned.
|
||||
Typically \f[B]EROFS\f[] (read\-only filesystem) or \f[B]ENOSPC\f[] (no
|
||||
space left on device) depending on the most recent reason for filtering
|
||||
a branch.
|
||||
.SS Policy descriptions
|
||||
.PP
|
||||
Because of the nature of the behavior the policies act differently
|
||||
depending on the function it is used with (based on the category).
|
||||
A policy\[aq]s behavior differs, as mentioned above, based on the
|
||||
function it is used with.
|
||||
Sometimes it really might not make sense to even offer certain policies
|
||||
because they are literally the same as others but it makes things a bit
|
||||
more uniform.
|
||||
In mergerfs 3.0 this might change.
|
||||
.PP
|
||||
.TS
|
||||
tab(@);
|
||||
@@ -917,38 +1043,6 @@ When \f[C]ioctl\f[] is used with an open file then it will use the file
|
||||
handle which was created at the original \f[C]open\f[] call.
|
||||
However, when using \f[C]ioctl\f[] with a directory mergerfs will use
|
||||
the \f[C]open\f[] policy to find the directory to act on.
|
||||
.SS unlink
|
||||
.PP
|
||||
In FUSE there is an opaque "file handle" which is created by
|
||||
\f[C]open\f[], \f[C]create\f[], or \f[C]opendir\f[], passed to the
|
||||
kernel, and then is passed back to the FUSE userland application by the
|
||||
kernel.
|
||||
Unfortunately, the FUSE kernel driver does not always send the file
|
||||
handle when it theoretically could/should.
|
||||
This complicates certain behaviors / workflows particularly in the high
|
||||
level API.
|
||||
As a result mergerfs is currently doing a few hacky things.
|
||||
.PP
|
||||
libfuse2 and libfuse3, when using the high level API, will rename names
|
||||
to \f[C]\&.fuse_hiddenXXXXXX\f[] if the file is open when unlinked or
|
||||
renamed over.
|
||||
It does this so the file is still available when a request referencing
|
||||
the now missing file is made.
|
||||
This file however keeps a \f[C]rmdir\f[] from succeeding and can be
|
||||
picked up by software reading directories.
|
||||
.PP
|
||||
The change mergerfs has done is that if a file is open when an unlink or
|
||||
rename happens it will open the file and keep it open till closed by all
|
||||
those who opened it prior.
|
||||
When a request comes in referencing that file and it doesn\[aq]t include
|
||||
a file handle it will instead use the file handle created at
|
||||
unlink/rename time.
|
||||
.PP
|
||||
This won\[aq]t result in technically proper behavior but close enough
|
||||
for many usecases.
|
||||
.PP
|
||||
The plan is to rewrite mergerfs to use the low level API so these
|
||||
invasive libfuse changes are no longer necessary.
|
||||
.SS rename & link
|
||||
.PP
|
||||
\f[B]NOTE:\f[] If you\[aq]re receiving errors from software when files
|
||||
@@ -1075,6 +1169,46 @@ included when checking the mount\[aq]s stats.
|
||||
.PP
|
||||
The options \f[C]statfs\f[] and \f[C]statfs_ignore\f[] can be used to
|
||||
modify \f[C]statfs\f[] behavior.
|
||||
.SH ERROR HANDLING
|
||||
.PP
|
||||
POSIX filesystem functions offer a single return code meaning that there
|
||||
is some complication regarding the handling of multiple branches as
|
||||
mergerfs does.
|
||||
It tries to handle errors in a way that would generally return
|
||||
meaningful values for that particular function.
|
||||
.SS chmod, chown, removexattr, setxattr, truncate, utimens
|
||||
.IP "1)" 3
|
||||
if no error: return 0 (success)
|
||||
.IP "2)" 3
|
||||
if no successes: return first error
|
||||
.IP "3)" 3
|
||||
if one of the files acted on was the same as the related search
|
||||
function: return its value
|
||||
.IP "4)" 3
|
||||
return 0 (success)
|
||||
.PP
|
||||
While doing this increases the complexity and cost of error handling,
|
||||
particularly step 3, this provides probably the most reasonable return
|
||||
value.
|
||||
.SS unlink, rmdir
|
||||
.IP "1)" 3
|
||||
if no errors: return 0 (success)
|
||||
.IP "2)" 3
|
||||
return first error
|
||||
.PP
|
||||
Older version of mergerfs would return success if any success occurred
|
||||
but for unlink and rmdir there are downstream assumptions that, while
|
||||
not impossible to occur, can confuse some software.
|
||||
.SS others
|
||||
.PP
|
||||
For search functions there is always a single thing acted on and as such
|
||||
whatever return value that comes from the single function call is
|
||||
returned.
|
||||
.PP
|
||||
For create functions \f[C]mkdir\f[], \f[C]mknod\f[], and
|
||||
\f[C]symlink\f[] which don\[aq]t return a file descriptor and therefore
|
||||
can have \f[C]all\f[] or \f[C]epall\f[] policies it will return success
|
||||
if any of the calls succeed and an error otherwise.
|
||||
.SH BUILD / UPDATE
|
||||
.PP
|
||||
\f[B]NOTE:\f[] Prebuilt packages can be found at and recommended for
|
||||
@@ -1099,7 +1233,7 @@ $\ wget\ https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs\-
|
||||
$\ cd\ mergerfs
|
||||
$\ sudo\ tools/install\-build\-pkgs
|
||||
$\ make\ deb
|
||||
$\ sudo\ dpkg\ \-i\ ../mergerfs_version_arch.deb
|
||||
$\ sudo\ dpkg\ \-i\ ../mergerfs_<version>_<arch>.deb
|
||||
\f[]
|
||||
.fi
|
||||
.SS RHEL / CentOS /Fedora
|
||||
@@ -1136,6 +1270,35 @@ make\ STATIC=1\ \ \ \ \ \ \ \ \ \-\ build\ static\ binary
|
||||
make\ LTO=1\ \ \ \ \ \ \ \ \ \ \ \ \-\ build\ with\ link\ time\ optimization
|
||||
\f[]
|
||||
.fi
|
||||
.SH UPGRADE
|
||||
.PP
|
||||
mergerfs can be upgraded live by mounting on top of the previous
|
||||
instance.
|
||||
Simply install the new version of mergerfs and follow the instructions
|
||||
below.
|
||||
.PP
|
||||
Add \f[C]nonempty\f[] to your mergerfs option list and call mergerfs
|
||||
again or if using \f[C]/etc/fstab\f[] call for it to mount again.
|
||||
Existing open files and such will continue to work fine though they
|
||||
won\[aq]t see runtime changes since any such change would be the new
|
||||
mount.
|
||||
If you plan on changing settings with the new mount you should / could
|
||||
apply those before mounting the new version.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
$\ sudo\ mount\ /mnt/mergerfs
|
||||
$\ mount\ |\ grep\ mergerfs
|
||||
media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
A problem with this approach is that the underlying instance will
|
||||
continue to run even if the software using it stop or are restarted.
|
||||
To work around this you can use a "lazy umount".
|
||||
Before mounting over top the mount point with the new instance of
|
||||
mergerfs issue: \f[C]umount\ \-l\ <mergerfs_mountpoint>\f[].
|
||||
.SH RUNTIME CONFIG
|
||||
.SS ioctl
|
||||
.PP
|
||||
@@ -1346,29 +1509,6 @@ A\ B\ C
|
||||
/mnt/b/full/path/to/A
|
||||
\f[]
|
||||
.fi
|
||||
.SH UPGRADE
|
||||
.PP
|
||||
mergerfs can be upgraded live by mounting on top of the previous
|
||||
version.
|
||||
Simply install the new version of mergerfs and follow the instructions
|
||||
below.
|
||||
.PP
|
||||
Add \f[C]nonempty\f[] to your mergerfs option list and call mergerfs
|
||||
again or if using \f[C]/etc/fstab\f[] call for it to mount again.
|
||||
Existing open files and such will continue to work fine though they
|
||||
won\[aq]t see runtime changes since any such change would be the new
|
||||
mount.
|
||||
If you plan on changing settings with the new mount you should / could
|
||||
apply those before mounting the new version.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
$\ sudo\ mount\ /mnt/mergerfs
|
||||
$\ mount\ |\ grep\ mergerfs
|
||||
media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
media\ on\ /mnt/mergerfs\ type\ fuse.mergerfs\ (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
\f[]
|
||||
.fi
|
||||
.SH TOOLING
|
||||
.IP \[bu] 2
|
||||
https://github.com/trapexit/mergerfs\-tools
|
||||
@@ -1390,9 +1530,6 @@ most filled to the least filled
|
||||
mergerfs.consolidate: move files within a single mergerfs directory to
|
||||
the drive with most free space
|
||||
.IP \[bu] 2
|
||||
mergerfs.mktrash: Creates FreeDesktop.org Trash specification compatible
|
||||
directories on a mergerfs mount
|
||||
.IP \[bu] 2
|
||||
https://github.com/trapexit/scorch
|
||||
.IP \[bu] 2
|
||||
scorch: A tool to help discover silent corruption of files and keep
|
||||
@@ -1638,6 +1775,11 @@ Replace \f[C]\-atime\f[] with \f[C]\-amin\f[] if you want minutes rather
|
||||
than days.
|
||||
May want to use the \f[C]fadvise\f[] / \f[C]\-\-drop\-cache\f[] version
|
||||
of rsync or run rsync with the tool "nocache".
|
||||
.PP
|
||||
\f[I]NOTE:\f[] The arguments to these scripts include the cache
|
||||
\f[B]drive\f[].
|
||||
Not the pool with the cache drive.
|
||||
You could have data loss if the source is the cache pool.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
@@ -1660,6 +1802,11 @@ find\ "${CACHE}"\ \-type\ f\ \-atime\ +${N}\ \-printf\ \[aq]%P\\n\[aq]\ |\ \\
|
||||
.PP
|
||||
Move the oldest file from the cache to the backing pool.
|
||||
Continue till below percentage threshold.
|
||||
.PP
|
||||
\f[I]NOTE:\f[] The arguments to these scripts include the cache
|
||||
\f[B]drive\f[].
|
||||
Not the pool with the cache drive.
|
||||
You could have data loss if the source is the cache pool.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
@@ -1682,7 +1829,7 @@ do
|
||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ head\ \-n\ 1\ |\ \\
|
||||
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ cut\ \-d\[aq]\ \[aq]\ \-f2\-)
|
||||
\ \ \ \ test\ \-n\ "${FILE}"
|
||||
\ \ \ \ rsync\ \-axqHAXWES\ \-\-preallocate\ \-\-remove\-source\-files\ "${CACHE}/./${FILE}"\ "${BACKING}/"
|
||||
\ \ \ \ rsync\ \-axqHAXWESR\ \-\-preallocate\ \-\-remove\-source\-files\ "${CACHE}/./${FILE}"\ "${BACKING}/"
|
||||
done
|
||||
\f[]
|
||||
.fi
|
||||
@@ -1832,39 +1979,55 @@ $\ dd\ if=/dev/zero\ of=/mnt/mergerfs/1GB.file\ bs=1M\ count=1024\ oflag=nocache
|
||||
$\ dd\ if=/mnt/mergerfs/1GB.file\ of=/dev/null\ bs=1M\ count=1024\ iflag=nocache\ conv=fdatasync\ status=progress
|
||||
\f[]
|
||||
.fi
|
||||
.SS other benchmarks
|
||||
.PP
|
||||
If you are attempting to benchmark other behaviors you must ensure you
|
||||
clear kernel caches before runs.
|
||||
In fact it would be a good deal to run before the read and write
|
||||
benchmarks as well just in case.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
sync
|
||||
echo\ 3\ |\ sudo\ tee\ /proc/sys/vm/drop_caches
|
||||
\f[]
|
||||
.fi
|
||||
.SH TIPS / NOTES
|
||||
.IP \[bu] 2
|
||||
This document is very literal and thorough.
|
||||
Unless there is a bug things work as described.
|
||||
If a suspected feature isn\[aq]t mentioned it doesn\[aq]t exist.
|
||||
If certain libfuse arguments aren\[aq]t listed they probably
|
||||
shouldn\[aq]t be used.
|
||||
.IP \[bu] 2
|
||||
Ensure you\[aq]re using the latest version.
|
||||
Few distros have the latest version.
|
||||
.IP \[bu] 2
|
||||
\f[B]use_ino\f[] will only work when used with mergerfs 2.18.0 and
|
||||
above.
|
||||
.IP \[bu] 2
|
||||
Run mergerfs as \f[C]root\f[] (with \f[B]allow_other\f[]) unless
|
||||
you\[aq]re merging paths which are owned by the same user otherwise
|
||||
strange permission issues may arise.
|
||||
you\[aq]re merging paths which are owned exclusively and fully by the
|
||||
same user otherwise strange permission issues may arise.
|
||||
mergerfs is designed and intended to be run as \f[C]root\f[].
|
||||
.IP \[bu] 2
|
||||
https://github.com/trapexit/backup\-and\-recovery\-howtos : A set of
|
||||
guides / howtos on creating a data storage system, backing it up,
|
||||
maintaining it, and recovering from failure.
|
||||
.IP \[bu] 2
|
||||
If you don\[aq]t see some directories and files you expect in a merged
|
||||
point or policies seem to skip drives be sure the user has permission to
|
||||
all the underlying directories.
|
||||
If you don\[aq]t see some directories and files you expect, policies
|
||||
seem to skip branches, you get strange permission errors, etc.
|
||||
be sure the underlying filesystems\[aq] permissions are all the same.
|
||||
Use \f[C]mergerfs.fsck\f[] to audit the drive for out of sync
|
||||
permissions.
|
||||
.IP \[bu] 2
|
||||
If you still have permission issues be sure you are using POSIX ACL
|
||||
compliant filesystems.
|
||||
mergerfs doesn\[aq]t generally make exceptions for FAT, NTFS, or other
|
||||
non\-POSIX filesystem.
|
||||
.IP \[bu] 2
|
||||
Do \f[B]not\f[] use \f[C]cache.files=off\f[] if you expect applications
|
||||
(such as rtorrent) to use mmap (http://linux.die.net/man/2/mmap) files.
|
||||
Shared mmap is not currently supported in FUSE w/ page caching disabled.
|
||||
Enabling \f[C]dropcacheonclose\f[] is recommended when
|
||||
\f[C]cache.files=partial|full|auto\-full\f[].
|
||||
.IP \[bu] 2
|
||||
Since POSIX functions give only a singular error or success its
|
||||
difficult to determine the proper behavior when applying the function to
|
||||
multiple targets.
|
||||
\f[B]mergerfs\f[] will return an error only if all attempts of an action
|
||||
fail.
|
||||
Any success will lead to a success returned.
|
||||
This means however that some odd situations may arise.
|
||||
.IP \[bu] 2
|
||||
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
|
||||
@@ -2254,7 +2417,7 @@ Setting \f[C]func.mkdir=epall\f[] can simplify managing path
|
||||
preservation for \f[C]create\f[].
|
||||
Or use \f[C]func.mkdir=rand\f[] if you\[aq]re interested in just
|
||||
grouping together directory content by drive.
|
||||
.SS Do hard links work?
|
||||
.SS Do hardlinks work?
|
||||
.PP
|
||||
Yes.
|
||||
You need to use \f[C]use_ino\f[] to support proper reporting of inodes
|
||||
@@ -2263,7 +2426,45 @@ See also the option \f[C]inodecalc\f[].
|
||||
.PP
|
||||
What mergerfs does not do is fake hard links across branches.
|
||||
Read the section "rename & link" for how it works.
|
||||
.SS Does mergerfs support CoW / copy\-on\-write?
|
||||
.PP
|
||||
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 between them.
|
||||
You should mount in the highest directory in the mergerfs pool that
|
||||
includes all the paths you need if you want links to work.
|
||||
.SS Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
||||
.PP
|
||||
Yes.
|
||||
They are completely unreleated pieces of software.
|
||||
.SS Can mergerfs run via Docker, Podman, Kubernetes, etc.
|
||||
.PP
|
||||
Yes.
|
||||
With Docker you\[aq]ll need to include
|
||||
\f[C]\-\-cap\-add=SYS_ADMIN\ \-\-device=/dev/fuse\ \-\-security\-opt=apparmor:unconfined\f[]
|
||||
or similar with other container runtimes.
|
||||
You should also be running it as root or given sufficient caps to change
|
||||
user and group identity as well as have root like filesystem
|
||||
permissions.
|
||||
.PP
|
||||
Keep in mind that you \f[B]MUST\f[] consider identity when using
|
||||
containers.
|
||||
For example: supplemental groups will be picked up from the container
|
||||
unless you properly manage users and groups by sharing relevant /etc
|
||||
files or by using some other means to share identity across containers.
|
||||
Similarly if you use "rootless" containers and user namespaces to do
|
||||
uid/gid translations you \f[B]MUST\f[] consider that while managing
|
||||
shared files.
|
||||
.PP
|
||||
Also, as mentioned by hotio (https://hotio.dev/containers/mergerfs),
|
||||
with Docker you should probably be mounting with
|
||||
\f[C]bind\-propagation\f[] set to \f[C]slave\f[].
|
||||
.SS Does mergerfs support CoW / copy\-on\-write / writes to read\-only
|
||||
filesystems?
|
||||
.PP
|
||||
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs
|
||||
or aufs sense.
|
||||
@@ -2273,6 +2474,10 @@ like hard link breaking (copy to temp file then rename over original)
|
||||
which can be useful when wanting to save space by hardlinking duplicate
|
||||
files but wish to treat each name as if it were a unique and separate
|
||||
file.
|
||||
.PP
|
||||
If you want to write to a read\-only filesystem you should look at
|
||||
overlayfs.
|
||||
You can always include the overlayfs mount into a mergerfs pool.
|
||||
.SS Why can\[aq]t I see my files / directories?
|
||||
.PP
|
||||
It\[aq]s almost always a permissions issue.
|
||||
@@ -2341,8 +2546,8 @@ Longer term the plan is to rewrite mergerfs to use the low level API.
|
||||
See above first.
|
||||
.PP
|
||||
If/when mergerfs is rewritten to use the low\-level API then it\[aq]ll
|
||||
be plausible to support system libfuse but till then its simply too much
|
||||
work to manage the differences across the versions.
|
||||
be plausible to support system libfuse but till then it\[aq]s simply too
|
||||
much work to manage the differences across the versions.
|
||||
.SS Why use mergerfs over mhddfs?
|
||||
.PP
|
||||
mhddfs is no longer maintained and has some known stability and security
|
||||
@@ -2593,42 +2798,47 @@ have been addressed already.\f[]
|
||||
.IP \[bu] 2
|
||||
Version of mergerfs: \f[C]mergerfs\ \-V\f[]
|
||||
.IP \[bu] 2
|
||||
mergerfs settings: from \f[C]/etc/fstab\f[] or command line execution
|
||||
mergerfs settings / arguments: from fstab, systemd unit, command line,
|
||||
etc.
|
||||
.IP \[bu] 2
|
||||
Version of Linux: \f[C]uname\ \-a\f[]
|
||||
Version of the OS: \f[C]uname\ \-a\f[]
|
||||
.IP \[bu] 2
|
||||
Versions of any additional software being used
|
||||
.IP \[bu] 2
|
||||
List of drives, their filesystems, and sizes (before and after issue):
|
||||
\f[C]df\ \-h\f[]
|
||||
List of branches, their filesystem types, sizes (before and after
|
||||
issue): \f[C]df\ \-h\f[]
|
||||
.IP \[bu] 2
|
||||
\f[B]All\f[] information about the relevant branches and paths:
|
||||
permissions, etc.
|
||||
permissions, ownership, etc.
|
||||
.IP \[bu] 2
|
||||
\f[B]All\f[] information about the client app making the requests:
|
||||
version, uid/gid
|
||||
.IP \[bu] 2
|
||||
Runtime environment: mostly are things running inside containers or not
|
||||
.IP \[bu] 2
|
||||
A \f[C]strace\f[] of the app having problems:
|
||||
.IP \[bu] 2
|
||||
\f[C]strace\ \-f\ \-o\ /tmp/app.strace.txt\ <cmd>\f[]
|
||||
\f[C]strace\ \-fvTtt\ \-s\ 256\ \-o\ /tmp/app.strace.txt\ <cmd>\f[]
|
||||
.IP \[bu] 2
|
||||
A \f[C]strace\f[] of mergerfs while the program is trying to do whatever
|
||||
it\[aq]s failing to do:
|
||||
.IP \[bu] 2
|
||||
\f[C]strace\ \-f\ \-p\ <mergerfsPID>\ \-o\ /tmp/mergerfs.strace.txt\f[]
|
||||
\f[C]strace\ \-fvTtt\ \-s\ 256\ \-p\ <mergerfsPID>\ \-o\ /tmp/mergerfs.strace.txt\f[]
|
||||
.IP \[bu] 2
|
||||
\f[B]Precise\f[] directions on replicating the issue.
|
||||
Do not leave \f[B]anything\f[] out.
|
||||
.IP \[bu] 2
|
||||
Try to recreate the problem in the simplest way using standard programs.
|
||||
Try to recreate the problem in the simplest way using standard programs:
|
||||
ln, mv, cp, ls, dd, etc.
|
||||
.SS Contact / Issue submission
|
||||
.IP \[bu] 2
|
||||
github.com: https://github.com/trapexit/mergerfs/issues
|
||||
.IP \[bu] 2
|
||||
email: trapexit\@spawn.link
|
||||
.IP \[bu] 2
|
||||
discord: https://discord.gg/MpAr69V
|
||||
.IP \[bu] 2
|
||||
twitter: https://twitter.com/_trapexit
|
||||
.IP \[bu] 2
|
||||
reddit: https://www.reddit.com/user/trapexit
|
||||
.IP \[bu] 2
|
||||
discord: https://discord.gg/MpAr69V
|
||||
.SS Support development
|
||||
.PP
|
||||
This software is free to use and released under a very liberal license
|
||||
@@ -2636,40 +2846,65 @@ This software is free to use and released under a very liberal license
|
||||
That said if you like this software and would like to support its
|
||||
development donations are welcome.
|
||||
.PP
|
||||
At the moment my preference would be GitHub Sponsors only because I am
|
||||
part of the matching program.
|
||||
That said please use whatever platform you prefer.
|
||||
.IP \[bu] 2
|
||||
PayPal: https://paypal.me/trapexit
|
||||
Crypto is fine in whatever protocol you prefer.
|
||||
My preferences for fiat would be GitHub Sponsors or PayPal though feel
|
||||
free to use any platform listed below.
|
||||
.IP \[bu] 2
|
||||
GitHub Sponsors: https://github.com/sponsors/trapexit
|
||||
.IP \[bu] 2
|
||||
PayPal: https://paypal.me/trapexit
|
||||
.IP \[bu] 2
|
||||
Patreon: https://www.patreon.com/trapexit
|
||||
.IP \[bu] 2
|
||||
SubscribeStar: https://www.subscribestar.com/trapexit
|
||||
BuyMeACoffee: https://buymeacoff.ee/trapexit
|
||||
.IP \[bu] 2
|
||||
Ko\-Fi: https://ko\-fi.com/trapexit
|
||||
.IP \[bu] 2
|
||||
Open Collective: https://opencollective.com/trapexit
|
||||
.IP \[bu] 2
|
||||
Bitcoin (BTC): 1DfoUd2m5WCxJAMvcFuvDpT4DR2gWX2PWb
|
||||
Bitcoin (BTC): bc1qu537hqlnmn2wawx9n7nws0dlkz55h0cd93ny28
|
||||
.IP \[bu] 2
|
||||
Bitcoin Cash (BCH): qrf257j0l09yxty4kur8dk2uma8p5vntdcpks72l8z
|
||||
Bitcoin Cash (BCH):
|
||||
bitcoincash:qqp0vh9v44us74gaggwjfv9y54zfjmmd7srlqxa3xt
|
||||
.IP \[bu] 2
|
||||
Ethereum (ETH): 0xb486C0270fF75872Fc51d85879b9c15C380E66CA
|
||||
Bitcoin SV (BSV): 1FkFuxRtt3f8LbkpeUKRZq7gKJFzGSGgZV
|
||||
.IP \[bu] 2
|
||||
Litecoin (LTC): LW1rvHRPWtm2NUEMhJpP4DjHZY1FaJ1WYs
|
||||
Bitcoin Gold (BTG): AaPuJgJeohPjkB3LxJM6NKGnaHoRJ8ieT3
|
||||
.IP \[bu] 2
|
||||
Litecoin (LTC): MJQzsHBdNnkyGqCFdcAdHYKugicBmfAXfQ
|
||||
.IP \[bu] 2
|
||||
Dogecoin (DOGE): DLJNLVe28vZ4SMQSxDJLBQBv57rGtUoWFh
|
||||
.IP \[bu] 2
|
||||
Ethereum (ETH): 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
||||
.IP \[bu] 2
|
||||
Any ERC20 Token: 0xB8d6d55c0319aacC327860d13f891427caEede7a
|
||||
.IP \[bu] 2
|
||||
Ethereum Classic (ETC): 0x2B6054428e69a1201B6555f7a2aEc0Fba01EAD9F
|
||||
.IP \[bu] 2
|
||||
Harmony (ONE): one1hrtd2hqrrx4vcvncvrgnlzg5yl9wahn66lq6rw
|
||||
(0xB8d6d55c0319aacC327860d13f891427caEede7a)
|
||||
.IP \[bu] 2
|
||||
Monero (XMR):
|
||||
8AuU7PeK1fVhGP9yug8fdgKBssvUQoBVFKGhtT5DzWQt7fcTKC1SUx3Eb7xCAiVt3McWJp2Z9gX2wU7SPhh1GfWYBTCs6SS
|
||||
45BBZMrJwPSaFwSoqLVNEggWR2BJJsXxz7bNz8FXnnFo3GyhVJFSCrCFSS7zYwDa9r1TmFmGMxQ2HTntuc11yZ9q1LeCE8f
|
||||
.IP \[bu] 2
|
||||
Basic Attention Token (BAT): 0xE651d4900B4C305284Da43E2e182e9abE149A87A
|
||||
Dash (DASH): XvsFrohu8tbjA4E8p7xsc86E2ADxLHGXHL
|
||||
.IP \[bu] 2
|
||||
Chia (XCH):
|
||||
xch18l7e2q34jtuyzkq0jg8vp7yrtnfwzly30w3yyhkk96um2ur4yqcq6p2een
|
||||
.IP \[bu] 2
|
||||
LBRY Credits (LBC): bFusyoZPkSuzM2Pr8mcthgvkymaosJZt5r
|
||||
.IP \[bu] 2
|
||||
Zcash (ZEC): t1ZwTgmbQF23DJrzqbAmw8kXWvU2xUkkhTt
|
||||
Ripple (XRP): r9f6aoxaGD8aymxqH89Ke1PCUPkNiFdZZC
|
||||
.IP \[bu] 2
|
||||
Zcoin (XZC): a8L5Vz35KdCQe7Y7urK2pcCGau7JsqZ5Gw
|
||||
Tezos (XTZ): tz1ZxerkbbALsuU9XGV9K9fFpuLWnKAGfc1C
|
||||
.IP \[bu] 2
|
||||
Zcash (ZEC): t1Zo1GGn2T3GrhKvgdtnTsTnWu6tCPaCaHG
|
||||
.IP \[bu] 2
|
||||
DigiByte (DGB): Sb8r1qTrryY9Sp4YkTE1eeKEGVzgArnE5N
|
||||
.IP \[bu] 2
|
||||
Namecoin (NMC): NDzb9FkoptGu5QbgetCkodJqo2zE1cTwyb
|
||||
.IP \[bu] 2
|
||||
Vertcoin (VTC): 3PYdhokAGXJwWrwHRoTywxG4iUDk6EHjKe
|
||||
.IP \[bu] 2
|
||||
Other crypto currencies: contact me for address
|
||||
.SH LINKS
|
||||
|
||||
+1
-365
@@ -18,21 +18,8 @@
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs_glob.hpp"
|
||||
#include "fs_realpathize.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "num.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using nonstd::optional;
|
||||
|
||||
|
||||
Branch::Branch(const uint64_t &default_minfreespace_)
|
||||
@@ -108,354 +95,3 @@ Branch::ro_or_nc(void) const
|
||||
return ((mode == Branch::Mode::RO) ||
|
||||
(mode == Branch::Mode::NC));
|
||||
}
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
void
|
||||
split(const std::string &s_,
|
||||
std::string *instr_,
|
||||
std::string *values_)
|
||||
{
|
||||
uint64_t offset;
|
||||
|
||||
offset = s_.find_first_of('/');
|
||||
*instr_ = s_.substr(0,offset);
|
||||
if(offset != std::string::npos)
|
||||
*values_ = s_.substr(offset);
|
||||
}
|
||||
}
|
||||
|
||||
Branches::Branches(const uint64_t &default_minfreespace_)
|
||||
: default_minfreespace(default_minfreespace_)
|
||||
{
|
||||
pthread_rwlock_init(&lock,NULL);
|
||||
}
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
int
|
||||
parse_mode(const string &str_,
|
||||
Branch::Mode *mode_)
|
||||
{
|
||||
if(str_ == "RW")
|
||||
*mode_ = Branch::Mode::RW;
|
||||
ef(str_ == "RO")
|
||||
*mode_ = Branch::Mode::RO;
|
||||
ef(str_ == "NC")
|
||||
*mode_ = Branch::Mode::NC;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_minfreespace(const string &str_,
|
||||
optional<uint64_t> *minfreespace_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t uint64;
|
||||
|
||||
rv = str::from(str_,&uint64);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
*minfreespace_ = uint64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_branch(const string &str_,
|
||||
string *glob_,
|
||||
Branch::Mode *mode_,
|
||||
optional<uint64_t> *minfreespace_)
|
||||
{
|
||||
int rv;
|
||||
string options;
|
||||
vector<string> v;
|
||||
|
||||
str::rsplit1(str_,'=',&v);
|
||||
switch(v.size())
|
||||
{
|
||||
case 1:
|
||||
*glob_ = v[0];
|
||||
*mode_ = Branch::Mode::RW;
|
||||
break;
|
||||
case 2:
|
||||
*glob_ = v[0];
|
||||
options = v[1];
|
||||
v.clear();
|
||||
str::split(options,',',&v);
|
||||
switch(v.size())
|
||||
{
|
||||
case 2:
|
||||
rv = l::parse_minfreespace(v[1],minfreespace_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
case 1:
|
||||
rv = l::parse_mode(v[0],mode_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
break;
|
||||
case 0:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse(const string &str_,
|
||||
const uint64_t &default_minfreespace_,
|
||||
BranchVec *branches_)
|
||||
{
|
||||
int rv;
|
||||
string glob;
|
||||
vector<string> globbed;
|
||||
optional<uint64_t> minfreespace;
|
||||
Branch branch(default_minfreespace_);
|
||||
|
||||
rv = l::parse_branch(str_,&glob,&branch.mode,&minfreespace);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(minfreespace.has_value())
|
||||
branch.set_minfreespace(minfreespace.value());
|
||||
|
||||
fs::glob(glob,&globbed);
|
||||
fs::realpathize(&globbed);
|
||||
for(size_t i = 0; i < globbed.size(); i++)
|
||||
{
|
||||
branch.path = globbed[i];
|
||||
branches_->push_back(branch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
set(const std::string &str_,
|
||||
Branches *branches_)
|
||||
{
|
||||
int rv;
|
||||
vector<string> paths;
|
||||
BranchVec tmp_branchvec;
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
rv = l::parse(paths[i],branches_->default_minfreespace,&tmp_branchvec);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->vec.clear();
|
||||
branches_->vec.insert(branches_->vec.end(),
|
||||
tmp_branchvec.begin(),
|
||||
tmp_branchvec.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
add_begin(const std::string &str_,
|
||||
Branches *branches_)
|
||||
{
|
||||
int rv;
|
||||
vector<string> paths;
|
||||
BranchVec tmp_branchvec;
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
rv = l::parse(paths[i],branches_->default_minfreespace,&tmp_branchvec);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->vec.insert(branches_->vec.begin(),
|
||||
tmp_branchvec.begin(),
|
||||
tmp_branchvec.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
add_end(const std::string &str_,
|
||||
Branches *branches_)
|
||||
{
|
||||
int rv;
|
||||
vector<string> paths;
|
||||
BranchVec tmp_branchvec;
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
rv = l::parse(paths[i],branches_->default_minfreespace,&tmp_branchvec);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->vec.insert(branches_->vec.end(),
|
||||
tmp_branchvec.begin(),
|
||||
tmp_branchvec.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_begin(BranchVec *branches_)
|
||||
{
|
||||
branches_->erase(branches_->begin());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_end(BranchVec *branches_)
|
||||
{
|
||||
branches_->pop_back();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_fnmatch(const std::string &str_,
|
||||
Branches *branches_)
|
||||
{
|
||||
vector<string> patterns;
|
||||
|
||||
str::split(str_,':',&patterns);
|
||||
|
||||
for(BranchVec::iterator i = branches_->vec.begin();
|
||||
i != branches_->vec.end();)
|
||||
{
|
||||
int match = FNM_NOMATCH;
|
||||
|
||||
for(vector<string>::const_iterator pi = patterns.begin();
|
||||
pi != patterns.end() && match != 0;
|
||||
++pi)
|
||||
{
|
||||
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
|
||||
}
|
||||
|
||||
i = ((match == 0) ? branches_->vec.erase(i) : (i+1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Branches::from_string(const std::string &s_)
|
||||
{
|
||||
rwlock::WriteGuard guard(lock);
|
||||
|
||||
std::string instr;
|
||||
std::string values;
|
||||
|
||||
l::split(s_,&instr,&values);
|
||||
|
||||
if(instr == "+")
|
||||
return l::add_end(values,this);
|
||||
if(instr == "+<")
|
||||
return l::add_begin(values,this);
|
||||
if(instr == "+>")
|
||||
return l::add_end(values,this);
|
||||
if(instr == "-")
|
||||
return l::erase_fnmatch(values,this);
|
||||
if(instr == "-<")
|
||||
return l::erase_begin(&vec);
|
||||
if(instr == "->")
|
||||
return l::erase_end(&vec);
|
||||
if(instr == "=")
|
||||
return l::set(values,this);
|
||||
if(instr.empty())
|
||||
return l::set(values,this);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
string
|
||||
Branches::to_string(void) const
|
||||
{
|
||||
rwlock::ReadGuard guard(lock);
|
||||
|
||||
string tmp;
|
||||
|
||||
for(size_t i = 0; i < vec.size(); i++)
|
||||
{
|
||||
const Branch &branch = vec[i];
|
||||
|
||||
tmp += branch.to_string();
|
||||
tmp += ':';
|
||||
}
|
||||
|
||||
if(*tmp.rbegin() == ':')
|
||||
tmp.erase(tmp.size() - 1);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void
|
||||
Branches::to_paths(vector<string> &vec_) const
|
||||
{
|
||||
rwlock::ReadGuard guard(lock);
|
||||
|
||||
for(size_t i = 0; i < vec.size(); i++)
|
||||
{
|
||||
const Branch &branch = vec[i];
|
||||
|
||||
vec_.push_back(branch.path);
|
||||
}
|
||||
}
|
||||
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
: _branches(b_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
SrcMounts::from_string(const std::string &s_)
|
||||
{
|
||||
return _branches.from_string(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
SrcMounts::to_string(void) const
|
||||
{
|
||||
rwlock::ReadGuard guard(_branches.lock);
|
||||
|
||||
std::string rv;
|
||||
|
||||
for(uint64_t i = 0; i < _branches.vec.size(); i++)
|
||||
{
|
||||
rv += _branches.vec[i].path;
|
||||
rv += ':';
|
||||
}
|
||||
|
||||
if(*rv.rbegin() == ':')
|
||||
rv.erase(rv.size() - 1);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
+22
-53
@@ -18,81 +18,50 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rwlock.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
class Branch : public ToFromString
|
||||
class Branch final : public ToFromString
|
||||
{
|
||||
public:
|
||||
Branch(const uint64_t &default_minfreespace);
|
||||
typedef std::vector<Branch> Vector;
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str);
|
||||
std::string to_string(void) const;
|
||||
Branch(const uint64_t &default_minfreespace_);
|
||||
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
INVALID,
|
||||
RO,
|
||||
RW,
|
||||
NC
|
||||
INVALID,
|
||||
RO,
|
||||
RW,
|
||||
NC
|
||||
};
|
||||
|
||||
public:
|
||||
Mode mode;
|
||||
std::string path;
|
||||
uint64_t minfreespace() const;
|
||||
|
||||
public:
|
||||
void set_minfreespace(const uint64_t minfreespace);
|
||||
|
||||
public:
|
||||
bool ro(void) const;
|
||||
bool nc(void) const;
|
||||
bool ro_or_nc(void) const;
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
uint64_t minfreespace() const;
|
||||
void set_minfreespace(const uint64_t);
|
||||
|
||||
public:
|
||||
Mode mode;
|
||||
std::string path;
|
||||
|
||||
private:
|
||||
nonstd::optional<uint64_t> _minfreespace;
|
||||
const uint64_t *_default_minfreespace;
|
||||
};
|
||||
|
||||
typedef std::vector<Branch> BranchVec;
|
||||
|
||||
class Branches : public ToFromString
|
||||
{
|
||||
public:
|
||||
Branches(const uint64_t &default_minfreespace_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str);
|
||||
std::string to_string(void) const;
|
||||
|
||||
public:
|
||||
void to_paths(std::vector<std::string> &vec) const;
|
||||
|
||||
public:
|
||||
mutable pthread_rwlock_t lock;
|
||||
BranchVec vec;
|
||||
const uint64_t &default_minfreespace;
|
||||
};
|
||||
|
||||
class SrcMounts : public ToFromString
|
||||
{
|
||||
public:
|
||||
SrcMounts(Branches &b_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str);
|
||||
std::string to_string(void) const;
|
||||
|
||||
private:
|
||||
Branches &_branches;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "branches.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs_glob.hpp"
|
||||
#include "fs_realpathize.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "num.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using nonstd::optional;
|
||||
|
||||
|
||||
Branches::Impl::Impl(const uint64_t &default_minfreespace_)
|
||||
: _default_minfreespace(default_minfreespace_)
|
||||
{
|
||||
}
|
||||
|
||||
Branches::Impl&
|
||||
Branches::Impl::operator=(Branches::Impl &rval_)
|
||||
{
|
||||
auto this_base = dynamic_cast<Branch::Vector*>(this);
|
||||
auto rval_base = dynamic_cast<Branch::Vector*>(&rval_);
|
||||
|
||||
*this_base = *rval_base;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Branches::Impl&
|
||||
Branches::Impl::operator=(Branches::Impl &&rval_)
|
||||
{
|
||||
auto this_base = dynamic_cast<Branch::Vector*>(this);
|
||||
auto rval_base = dynamic_cast<Branch::Vector*>(&rval_);
|
||||
|
||||
*this_base = std::move(*rval_base);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const
|
||||
uint64_t&
|
||||
Branches::Impl::minfreespace(void) const
|
||||
{
|
||||
return _default_minfreespace;
|
||||
}
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
void
|
||||
split(const std::string &s_,
|
||||
std::string *instr_,
|
||||
std::string *values_)
|
||||
{
|
||||
uint64_t offset;
|
||||
|
||||
offset = s_.find_first_not_of("+<>-=");
|
||||
if(offset > 1)
|
||||
offset = 2;
|
||||
*instr_ = s_.substr(0,offset);
|
||||
if(offset != std::string::npos)
|
||||
*values_ = s_.substr(offset);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_mode(const string &str_,
|
||||
Branch::Mode *mode_)
|
||||
{
|
||||
if(str_ == "RW")
|
||||
*mode_ = Branch::Mode::RW;
|
||||
ef(str_ == "RO")
|
||||
*mode_ = Branch::Mode::RO;
|
||||
ef(str_ == "NC")
|
||||
*mode_ = Branch::Mode::NC;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_minfreespace(const string &str_,
|
||||
optional<uint64_t> *minfreespace_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t uint64;
|
||||
|
||||
rv = str::from(str_,&uint64);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
*minfreespace_ = uint64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_branch(const string &str_,
|
||||
string *glob_,
|
||||
Branch::Mode *mode_,
|
||||
optional<uint64_t> *minfreespace_)
|
||||
{
|
||||
int rv;
|
||||
string options;
|
||||
vector<string> v;
|
||||
|
||||
str::rsplit1(str_,'=',&v);
|
||||
switch(v.size())
|
||||
{
|
||||
case 1:
|
||||
*glob_ = v[0];
|
||||
*mode_ = Branch::Mode::RW;
|
||||
break;
|
||||
case 2:
|
||||
*glob_ = v[0];
|
||||
options = v[1];
|
||||
v.clear();
|
||||
str::split(options,',',&v);
|
||||
switch(v.size())
|
||||
{
|
||||
case 2:
|
||||
rv = l::parse_minfreespace(v[1],minfreespace_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
case 1:
|
||||
rv = l::parse_mode(v[0],mode_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
break;
|
||||
case 0:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse(const string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
string glob;
|
||||
StrVec paths;
|
||||
optional<uint64_t> minfreespace;
|
||||
Branch branch(branches_->minfreespace());
|
||||
|
||||
rv = l::parse_branch(str_,&glob,&branch.mode,&minfreespace);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(minfreespace.has_value())
|
||||
branch.set_minfreespace(minfreespace.value());
|
||||
|
||||
fs::glob(glob,&paths);
|
||||
fs::realpathize(&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
branch.path = path;
|
||||
branches_->push_back(branch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
set(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
StrVec paths;
|
||||
Branches::Impl tmp_branches(branches_->minfreespace());
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
rv = l::parse(path,&tmp_branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
*branches_ = std::move(tmp_branches);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
add_begin(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
vector<string> paths;
|
||||
Branches::Impl tmp_branches(branches_->minfreespace());
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
rv = l::parse(path,&tmp_branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->insert(branches_->begin(),
|
||||
tmp_branches.begin(),
|
||||
tmp_branches.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
add_end(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
StrVec paths;
|
||||
Branches::Impl tmp_branches(branches_->minfreespace());
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
rv = l::parse(path,&tmp_branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->insert(branches_->end(),
|
||||
tmp_branches.begin(),
|
||||
tmp_branches.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_begin(Branches::Impl *branches_)
|
||||
{
|
||||
branches_->erase(branches_->begin());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_end(Branches::Impl *branches_)
|
||||
{
|
||||
branches_->pop_back();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_fnmatch(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
StrVec patterns;
|
||||
|
||||
str::split(str_,':',&patterns);
|
||||
for(auto i = branches_->begin(); i != branches_->end();)
|
||||
{
|
||||
int match = FNM_NOMATCH;
|
||||
|
||||
for(auto pi = patterns.cbegin(); pi != patterns.cend() && match != 0; ++pi)
|
||||
{
|
||||
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
|
||||
}
|
||||
|
||||
i = ((match == 0) ? branches_->erase(i) : (i+1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Branches::Impl::from_string(const std::string &s_)
|
||||
{
|
||||
std::string instr;
|
||||
std::string values;
|
||||
|
||||
l::split(s_,&instr,&values);
|
||||
|
||||
if(instr == "+")
|
||||
return l::add_end(values,this);
|
||||
if(instr == "+<")
|
||||
return l::add_begin(values,this);
|
||||
if(instr == "+>")
|
||||
return l::add_end(values,this);
|
||||
if(instr == "-")
|
||||
return l::erase_fnmatch(values,this);
|
||||
if(instr == "-<")
|
||||
return l::erase_begin(this);
|
||||
if(instr == "->")
|
||||
return l::erase_end(this);
|
||||
if(instr == "=")
|
||||
return l::set(values,this);
|
||||
if(instr.empty())
|
||||
return l::set(values,this);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
std::string
|
||||
Branches::Impl::to_string(void) const
|
||||
{
|
||||
string tmp;
|
||||
|
||||
if(empty())
|
||||
return tmp;
|
||||
|
||||
for(auto &branch : *this)
|
||||
{
|
||||
tmp += branch.to_string();
|
||||
tmp += ':';
|
||||
}
|
||||
|
||||
tmp.pop_back();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void
|
||||
Branches::Impl::to_paths(StrVec &paths_) const
|
||||
{
|
||||
for(auto &branch : *this)
|
||||
{
|
||||
paths_.push_back(branch.path);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Branches::from_string(const std::string &str_)
|
||||
{
|
||||
int rv;
|
||||
Branches::Ptr impl;
|
||||
Branches::Ptr new_impl;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(_mutex);
|
||||
impl = _impl;
|
||||
}
|
||||
|
||||
new_impl = std::make_shared<Branches::Impl>(impl->minfreespace());
|
||||
*new_impl = *impl;
|
||||
|
||||
rv = new_impl->from_string(str_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(_mutex);
|
||||
_impl = new_impl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string
|
||||
Branches::to_string(void) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(_mutex);
|
||||
|
||||
return _impl->to_string();
|
||||
}
|
||||
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
: _branches(b_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
SrcMounts::from_string(const std::string &s_)
|
||||
{
|
||||
return _branches.from_string(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
SrcMounts::to_string(void) const
|
||||
{
|
||||
std::string rv;
|
||||
Branches::CPtr branches = _branches;
|
||||
|
||||
if(branches->empty())
|
||||
return rv;
|
||||
|
||||
for(const auto &branch : *branches)
|
||||
{
|
||||
rv += branch.path;
|
||||
rv += ':';
|
||||
}
|
||||
|
||||
rv.pop_back();
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "branch.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class Branches final : public ToFromString
|
||||
{
|
||||
public:
|
||||
class Impl final : public ToFromString, public Branch::Vector
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<Impl> Ptr;
|
||||
typedef std::shared_ptr<const Impl> CPtr;
|
||||
|
||||
public:
|
||||
Impl(const uint64_t &default_minfreespace_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
const uint64_t& minfreespace(void) const;
|
||||
void to_paths(StrVec &strvec) const;
|
||||
|
||||
public:
|
||||
Impl& operator=(Impl &impl_);
|
||||
Impl& operator=(Impl &&impl_);
|
||||
|
||||
private:
|
||||
const uint64_t &_default_minfreespace;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef Branches::Impl::Ptr Ptr;
|
||||
typedef Branches::Impl::CPtr CPtr;
|
||||
|
||||
public:
|
||||
Branches(const uint64_t &default_minfreespace_)
|
||||
: _impl(std::make_shared<Impl>(default_minfreespace_))
|
||||
{}
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
operator CPtr() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
CPtr operator->() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
|
||||
private:
|
||||
mutable std::mutex _mutex;
|
||||
Ptr _impl;
|
||||
};
|
||||
|
||||
class SrcMounts : public ToFromString
|
||||
{
|
||||
public:
|
||||
SrcMounts(Branches &b_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
private:
|
||||
Branches &_branches;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "category.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
int
|
||||
Category::Base::from_string(const std::string &s_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
for(auto func : funcs)
|
||||
{
|
||||
rv = func->from_string(s_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
Category::Base::to_string(void) const
|
||||
{
|
||||
std::set<std::string> rv;
|
||||
|
||||
for(auto func : funcs)
|
||||
rv.insert(func->to_string());
|
||||
|
||||
return str::join(rv,',');
|
||||
}
|
||||
+88
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
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
|
||||
@@ -16,9 +16,92 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class Category
|
||||
#include "tofrom_string.hpp"
|
||||
#include "funcs.hpp"
|
||||
#include "func.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Category
|
||||
{
|
||||
class Base : public ToFromString
|
||||
{
|
||||
ACTION,
|
||||
CREATE,
|
||||
SEARCH
|
||||
public:
|
||||
int from_string(const std::string &s) final;
|
||||
std::string to_string() const final;
|
||||
|
||||
protected:
|
||||
std::vector<ToFromString*> funcs;
|
||||
};
|
||||
|
||||
class Action final : public Base
|
||||
{
|
||||
private:
|
||||
Action();
|
||||
|
||||
public:
|
||||
Action(Funcs &funcs_)
|
||||
{
|
||||
funcs.push_back(&funcs_.chmod);
|
||||
funcs.push_back(&funcs_.chown);
|
||||
funcs.push_back(&funcs_.link);
|
||||
funcs.push_back(&funcs_.removexattr);
|
||||
funcs.push_back(&funcs_.rename);
|
||||
funcs.push_back(&funcs_.rmdir);
|
||||
funcs.push_back(&funcs_.setxattr);
|
||||
funcs.push_back(&funcs_.truncate);
|
||||
funcs.push_back(&funcs_.unlink);
|
||||
funcs.push_back(&funcs_.utimens);
|
||||
}
|
||||
};
|
||||
|
||||
class Create final : public Base
|
||||
{
|
||||
private:
|
||||
Create();
|
||||
|
||||
public:
|
||||
Create(Funcs &funcs_)
|
||||
{
|
||||
funcs.push_back(&funcs_.create);
|
||||
funcs.push_back(&funcs_.mkdir);
|
||||
funcs.push_back(&funcs_.mknod);
|
||||
funcs.push_back(&funcs_.symlink);
|
||||
}
|
||||
};
|
||||
|
||||
class Search final : public Base
|
||||
{
|
||||
private:
|
||||
Search();
|
||||
|
||||
public:
|
||||
Search(Funcs &funcs_)
|
||||
{
|
||||
funcs.push_back(&funcs_.access);
|
||||
funcs.push_back(&funcs_.getattr);
|
||||
funcs.push_back(&funcs_.getxattr);
|
||||
funcs.push_back(&funcs_.listxattr);
|
||||
funcs.push_back(&funcs_.open);
|
||||
funcs.push_back(&funcs_.readlink);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Categories final
|
||||
{
|
||||
private:
|
||||
Categories();
|
||||
|
||||
public:
|
||||
Categories(Funcs &funcs_)
|
||||
: action(funcs_),
|
||||
create(funcs_),
|
||||
search(funcs_)
|
||||
{}
|
||||
|
||||
public:
|
||||
Category::Action action;
|
||||
Category::Create create;
|
||||
Category::Search search;
|
||||
};
|
||||
|
||||
+181
-55
@@ -20,14 +20,18 @@
|
||||
#include "from_string.hpp"
|
||||
#include "num.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "str.hpp"
|
||||
#include "to_string.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -37,6 +41,11 @@ using std::string;
|
||||
|
||||
#define IFERT(S) if(S == s_) return true
|
||||
|
||||
const std::string CONTROLFILE = "/.mergerfs";
|
||||
|
||||
Config Config::_singleton;
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
@@ -60,49 +69,48 @@ namespace l
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
:
|
||||
open_cache(),
|
||||
|
||||
controlfile("/.mergerfs"),
|
||||
|
||||
async_read(true),
|
||||
auto_cache(false),
|
||||
branches(minfreespace),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::LIBFUSE),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
category(func),
|
||||
direct_io(false),
|
||||
dropcacheonclose(false),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size(FUSE_MAX_MAX_PAGES),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
link_cow(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
mount(),
|
||||
moveonenospc(false),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
nullrw(false),
|
||||
pid(::getpid()),
|
||||
posix_acl(false),
|
||||
readdir(ReadDir::ENUM::POSIX),
|
||||
readdirplus(false),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
threads(0),
|
||||
version(MERGERFS_VERSION),
|
||||
writeback_cache(false),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH)
|
||||
: async_read(true),
|
||||
auto_cache(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
branches(minfreespace),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::LIBFUSE),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
category(func),
|
||||
direct_io(false),
|
||||
dropcacheonclose(false),
|
||||
fsname(),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
func(),
|
||||
fuse_msg_size(FUSE_MAX_MAX_PAGES),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
link_cow(false),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
log_metrics(false),
|
||||
mount(),
|
||||
moveonenospc(false),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
nullrw(false),
|
||||
pid(::getpid()),
|
||||
posix_acl(false),
|
||||
readdir(ReadDir::ENUM::POSIX),
|
||||
readdirplus(false),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
threads(0),
|
||||
version(MERGERFS_VERSION),
|
||||
writeback_cache(false),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH)
|
||||
{
|
||||
_map["async_read"] = &async_read;
|
||||
_map["auto_cache"] = &auto_cache;
|
||||
@@ -120,6 +128,7 @@ Config::Config()
|
||||
_map["category.search"] = &category.search;
|
||||
_map["direct_io"] = &direct_io;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_map["fsname"] = &fsname;
|
||||
_map["func.access"] = &func.access;
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
@@ -146,6 +155,8 @@ Config::Config()
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel_cache"] = &kernel_cache;
|
||||
_map["link_cow"] = &link_cow;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["log.metrics"] = &log_metrics;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &mount;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
@@ -155,6 +166,7 @@ Config::Config()
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
// _map["readdir"] = &readdir;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["security_capability"] = &security_capability;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
@@ -166,17 +178,22 @@ Config::Config()
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
const
|
||||
Config&
|
||||
Config::ro(void)
|
||||
Config::operator=(const Config &cfg_)
|
||||
{
|
||||
return *((Config*)fuse_get_context()->private_data);
|
||||
}
|
||||
int rv;
|
||||
std::string val;
|
||||
|
||||
Config&
|
||||
Config::rw(void)
|
||||
{
|
||||
return *((Config*)fuse_get_context()->private_data);
|
||||
for(auto &kv : _map)
|
||||
{
|
||||
rv = cfg_.get(kv.first,&val);
|
||||
if(rv)
|
||||
continue;
|
||||
|
||||
kv.second->from_string(val);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -254,11 +271,80 @@ Config::set(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
if(l::readonly(key_))
|
||||
return -EINVAL;
|
||||
return -EROFS;
|
||||
|
||||
return set_raw(key_,value_);
|
||||
}
|
||||
|
||||
int
|
||||
Config::set(const std::string &kv_)
|
||||
{
|
||||
std::string key;
|
||||
std::string val;
|
||||
|
||||
str::splitkv(kv_,'=',&key,&val);
|
||||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
return set(key,val);
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_stream(std::istream &istrm_,
|
||||
ErrVec *errs_)
|
||||
{
|
||||
int rv;
|
||||
std::string line;
|
||||
std::string key;
|
||||
std::string val;
|
||||
Config newcfg;
|
||||
|
||||
newcfg = *this;
|
||||
|
||||
while(std::getline(istrm_,line,'\n'))
|
||||
{
|
||||
line = str::trim(line);
|
||||
if(!line.empty() && (line[0] == '#'))
|
||||
continue;
|
||||
|
||||
str::splitkv(line,'=',&key,&val);
|
||||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
rv = newcfg.set(key,val);
|
||||
if(rv < 0)
|
||||
errs_->push_back({rv,key});
|
||||
}
|
||||
|
||||
if(!errs_->empty())
|
||||
return -EINVAL;
|
||||
|
||||
*this = newcfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_file(const std::string &filepath_,
|
||||
ErrVec *errs_)
|
||||
{
|
||||
int rv;
|
||||
std::ifstream ifstrm;
|
||||
|
||||
ifstrm.open(filepath_);
|
||||
if(!ifstrm.good())
|
||||
{
|
||||
errs_->push_back({-errno,filepath_});
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rv = from_stream(ifstrm,errs_);
|
||||
|
||||
ifstrm.close();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config &c_)
|
||||
@@ -268,7 +354,47 @@ operator<<(std::ostream &os_,
|
||||
|
||||
for(i = c_._map.begin(), ei = c_._map.end(); i != ei; ++i)
|
||||
{
|
||||
os_ << i->first << '=' << i->second << '\n';
|
||||
os_ << i->first << '=' << i->second->to_string() << std::endl;
|
||||
}
|
||||
|
||||
return os_;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
std::string
|
||||
err2str(const int err_)
|
||||
{
|
||||
switch(err_)
|
||||
{
|
||||
case 0:
|
||||
return std::string();
|
||||
case -EINVAL:
|
||||
return "invalid value";
|
||||
case -ENOATTR:
|
||||
return "unknown option";
|
||||
case -EROFS:
|
||||
return "read-only option";
|
||||
default:
|
||||
return strerror(-err_);
|
||||
}
|
||||
|
||||
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_;
|
||||
|
||||
+96
-13
@@ -16,29 +16,35 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "config_link_exdev.hpp"
|
||||
#include "config_log_metrics.hpp"
|
||||
#include "config_moveonenospc.hpp"
|
||||
#include "config_nfsopenhack.hpp"
|
||||
#include "config_readdir.hpp"
|
||||
#include "config_rename_exdev.hpp"
|
||||
#include "config_statfs.hpp"
|
||||
#include "config_statfsignore.hpp"
|
||||
#include "config_xattr.hpp"
|
||||
#include "enum.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "func_category.hpp"
|
||||
#include "funcs.hpp"
|
||||
#include "policy.hpp"
|
||||
#include "policy_cache.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "tofrom_wrapper.hpp"
|
||||
|
||||
#include <fuse.h>
|
||||
#include "fuse.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
@@ -47,20 +53,55 @@ typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
|
||||
extern const std::string CONTROLFILE;
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
struct Err
|
||||
{
|
||||
int err;
|
||||
std::string str;
|
||||
};
|
||||
|
||||
typedef std::vector<Err> ErrVec;
|
||||
|
||||
public:
|
||||
class Read
|
||||
{
|
||||
public:
|
||||
Read();
|
||||
|
||||
public:
|
||||
inline const Config* operator->() const;
|
||||
|
||||
private:
|
||||
const Config &_cfg;
|
||||
};
|
||||
|
||||
public:
|
||||
class Write
|
||||
{
|
||||
public:
|
||||
Write();
|
||||
|
||||
public:
|
||||
Config* operator->();
|
||||
|
||||
private:
|
||||
Config &_cfg;
|
||||
};
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
public:
|
||||
mutable PolicyCache open_cache;
|
||||
|
||||
public:
|
||||
const std::string controlfile;
|
||||
Config& operator=(const Config&);
|
||||
|
||||
public:
|
||||
ConfigBOOL async_read;
|
||||
ConfigBOOL auto_cache;
|
||||
ConfigUINT64 minfreespace;
|
||||
Branches branches;
|
||||
ConfigUINT64 cache_attr;
|
||||
ConfigUINT64 cache_entry;
|
||||
@@ -69,17 +110,19 @@ public:
|
||||
ConfigBOOL cache_readdir;
|
||||
ConfigUINT64 cache_statfs;
|
||||
ConfigBOOL cache_symlinks;
|
||||
FuncCategories category;
|
||||
Categories category;
|
||||
ConfigBOOL direct_io;
|
||||
ConfigBOOL dropcacheonclose;
|
||||
ConfigSTR fsname;
|
||||
FollowSymlinks follow_symlinks;
|
||||
Funcs func;
|
||||
ConfigUINT64 fuse_msg_size;
|
||||
ConfigBOOL ignorepponrename;
|
||||
InodeCalc inodecalc;
|
||||
ConfigBOOL kernel_cache;
|
||||
ConfigBOOL link_cow;
|
||||
ConfigUINT64 minfreespace;
|
||||
LinkEXDEV link_exdev;
|
||||
LogMetrics log_metrics;
|
||||
ConfigSTR mount;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
@@ -88,6 +131,7 @@ public:
|
||||
ConfigBOOL posix_acl;
|
||||
ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
RenameEXDEV rename_exdev;
|
||||
ConfigBOOL security_capability;
|
||||
SrcMounts srcmounts;
|
||||
StatFS statfs;
|
||||
@@ -112,11 +156,50 @@ 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:
|
||||
static const Config &ro(void);
|
||||
static Config &rw(void);
|
||||
int from_stream(std::istream &istrm, ErrVec *errs);
|
||||
int from_file(const std::string &filepath, ErrVec *errs);
|
||||
|
||||
private:
|
||||
Str2TFStrMap _map;
|
||||
|
||||
private:
|
||||
static Config _singleton;
|
||||
|
||||
public:
|
||||
friend class Read;
|
||||
friend class Write;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &s,const Config::ErrVec &ev);
|
||||
|
||||
inline
|
||||
Config::Read::Read()
|
||||
: _cfg(Config::_singleton)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
const
|
||||
Config*
|
||||
Config::Read::operator->() const
|
||||
{
|
||||
return &_cfg;
|
||||
}
|
||||
|
||||
inline
|
||||
Config::Write::Write()
|
||||
: _cfg(Config::_singleton)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
Config*
|
||||
Config::Write::operator->()
|
||||
{
|
||||
return &_cfg;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class CacheFilesEnum
|
||||
{
|
||||
LIBFUSE,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
FollowSymlinks::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case FollowSymlinks::ENUM::NEVER:
|
||||
return "never";
|
||||
case FollowSymlinks::ENUM::DIRECTORY:
|
||||
return "directory";
|
||||
case FollowSymlinks::ENUM::REGULAR:
|
||||
return "regular";
|
||||
case FollowSymlinks::ENUM::ALL:
|
||||
return "all";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
FollowSymlinks::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "never")
|
||||
_data = FollowSymlinks::ENUM::NEVER;
|
||||
ef(s_ == "directory")
|
||||
_data = FollowSymlinks::ENUM::DIRECTORY;
|
||||
ef(s_ == "regular")
|
||||
_data = FollowSymlinks::ENUM::REGULAR;
|
||||
ef(s_ == "all")
|
||||
_data = FollowSymlinks::ENUM::ALL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 "enum.hpp"
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
typedef Enum<FollowSymlinksEnum> FollowSymlinks;
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
|
||||
|
||||
InodeCalc::InodeCalc(const std::string &s_)
|
||||
{
|
||||
fs::inode::set_algo(s_);
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
|
||||
class InodeCalc : public ToFromString
|
||||
{
|
||||
public:
|
||||
InodeCalc(const std::string &);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const;
|
||||
int from_string(const std::string &);
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string &) final;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "config_link_exdev.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
LinkEXDEV::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case LinkEXDEV::ENUM::PASSTHROUGH:
|
||||
return "passthrough";
|
||||
case LinkEXDEV::ENUM::REL_SYMLINK:
|
||||
return "rel-symlink";
|
||||
case LinkEXDEV::ENUM::ABS_BASE_SYMLINK:
|
||||
return "abs-base-symlink";
|
||||
case LinkEXDEV::ENUM::ABS_POOL_SYMLINK:
|
||||
return "abs-pool-symlink";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
LinkEXDEV::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "passthrough")
|
||||
_data = LinkEXDEV::ENUM::PASSTHROUGH;
|
||||
ef(s_ == "rel-symlink")
|
||||
_data = LinkEXDEV::ENUM::REL_SYMLINK;
|
||||
ef(s_ == "abs-base-symlink")
|
||||
_data = LinkEXDEV::ENUM::ABS_BASE_SYMLINK;
|
||||
ef(s_ == "abs-pool-symlink")
|
||||
_data = LinkEXDEV::ENUM::ABS_POOL_SYMLINK;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "enum.hpp"
|
||||
|
||||
enum class LinkEXDEVEnum
|
||||
{
|
||||
PASSTHROUGH,
|
||||
REL_SYMLINK,
|
||||
ABS_BASE_SYMLINK,
|
||||
ABS_POOL_SYMLINK
|
||||
};
|
||||
typedef Enum<LinkEXDEVEnum> LinkEXDEV;
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "config_log_metrics.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "to_string.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
LogMetrics::LogMetrics(const bool val_)
|
||||
{
|
||||
fuse_log_metrics_set(val_);
|
||||
}
|
||||
|
||||
std::string
|
||||
LogMetrics::to_string(void) const
|
||||
{
|
||||
bool val;
|
||||
|
||||
val = fuse_log_metrics_get();
|
||||
|
||||
return str::to(val);
|
||||
}
|
||||
|
||||
int
|
||||
LogMetrics::from_string(const std::string &s_)
|
||||
{
|
||||
int rv;
|
||||
bool val;
|
||||
|
||||
rv = str::from(s_,&val);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
fuse_log_metrics_set(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Copyright (c) 2021, 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
|
||||
@@ -18,52 +18,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "funcs.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class FuncCategory : public ToFromString
|
||||
class LogMetrics : public ToFromString
|
||||
{
|
||||
public:
|
||||
int from_string(const std::string &);
|
||||
std::string to_string(void) const;
|
||||
|
||||
protected:
|
||||
std::vector<Func*> _funcs;
|
||||
};
|
||||
|
||||
class FuncCategoryCreate : public FuncCategory
|
||||
{
|
||||
public:
|
||||
FuncCategoryCreate(Funcs &funcs_);
|
||||
};
|
||||
|
||||
class FuncCategoryAction : public FuncCategory
|
||||
{
|
||||
public:
|
||||
FuncCategoryAction(Funcs &funcs_);
|
||||
};
|
||||
|
||||
class FuncCategorySearch : public FuncCategory
|
||||
{
|
||||
public:
|
||||
FuncCategorySearch(Funcs &funcs_);
|
||||
};
|
||||
|
||||
class FuncCategories
|
||||
{
|
||||
public:
|
||||
FuncCategories(Funcs &funcs_)
|
||||
: action(funcs_),
|
||||
create(funcs_),
|
||||
search(funcs_)
|
||||
{
|
||||
}
|
||||
LogMetrics(const bool);
|
||||
|
||||
public:
|
||||
FuncCategoryAction action;
|
||||
FuncCategoryCreate create;
|
||||
FuncCategorySearch search;
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string &) final;
|
||||
};
|
||||
@@ -21,12 +21,13 @@
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
|
||||
int
|
||||
MoveOnENOSPC::from_string(const std::string &s_)
|
||||
{
|
||||
int rv;
|
||||
std::string s;
|
||||
const Policy *tmp;
|
||||
Policy::CreateImpl *tmp;
|
||||
|
||||
rv = str::from(s_,&enabled);
|
||||
if((rv == 0) && (enabled == true))
|
||||
@@ -36,8 +37,8 @@ MoveOnENOSPC::from_string(const std::string &s_)
|
||||
else
|
||||
return 0;
|
||||
|
||||
tmp = &Policy::find(s);
|
||||
if(tmp == Policy::invalid)
|
||||
tmp = Policies::Create::find(s);
|
||||
if(tmp == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
policy = tmp;
|
||||
@@ -50,6 +51,6 @@ std::string
|
||||
MoveOnENOSPC::to_string(void) const
|
||||
{
|
||||
if(enabled)
|
||||
return policy->to_string();
|
||||
return policy.name();
|
||||
return "false";
|
||||
}
|
||||
|
||||
@@ -19,26 +19,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "policy.hpp"
|
||||
#include "policies.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class MoveOnENOSPC : public ToFromString
|
||||
{
|
||||
public:
|
||||
MoveOnENOSPC(const bool enabled_)
|
||||
: enabled(enabled_)
|
||||
: enabled(enabled_),
|
||||
policy(&Policies::Create::mfs)
|
||||
{
|
||||
policy = (enabled ?
|
||||
&Policy::mfs :
|
||||
&Policy::invalid);
|
||||
}
|
||||
|
||||
public:
|
||||
int from_string(const std::string &s);
|
||||
std::string to_string() const;
|
||||
int from_string(const std::string &s) final;
|
||||
std::string to_string() const final;
|
||||
|
||||
public:
|
||||
bool enabled;
|
||||
const Policy *policy;
|
||||
Policy::Create policy;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
int
|
||||
NFSOpenHack::from_string(const std::string &s_)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class NFSOpenHackEnum
|
||||
{
|
||||
OFF,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
int
|
||||
ReadDir::from_string(const std::string &s_)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class ReadDirEnum
|
||||
{
|
||||
POSIX,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "config_rename_exdev.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
RenameEXDEV::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case RenameEXDEV::ENUM::PASSTHROUGH:
|
||||
return "passthrough";
|
||||
case RenameEXDEV::ENUM::REL_SYMLINK:
|
||||
return "rel-symlink";
|
||||
case RenameEXDEV::ENUM::ABS_SYMLINK:
|
||||
return "abs-symlink";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
RenameEXDEV::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "passthrough")
|
||||
_data = RenameEXDEV::ENUM::PASSTHROUGH;
|
||||
ef(s_ == "rel-symlink")
|
||||
_data = RenameEXDEV::ENUM::REL_SYMLINK;
|
||||
ef(s_ == "abs-symlink")
|
||||
_data = RenameEXDEV::ENUM::ABS_SYMLINK;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, 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 "enum.hpp"
|
||||
|
||||
enum class RenameEXDEVEnum
|
||||
{
|
||||
PASSTHROUGH,
|
||||
REL_SYMLINK,
|
||||
ABS_SYMLINK
|
||||
};
|
||||
typedef Enum<RenameEXDEVEnum> RenameEXDEV;
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
std::string
|
||||
StatFS::to_string() const
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class StatFSEnum
|
||||
{
|
||||
BASE,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
std::string
|
||||
StatFSIgnore::to_string() const
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
std::string
|
||||
XAttr::to_string() const
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "enum.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
enum class XAttrEnum
|
||||
{
|
||||
PASSTHROUGH = 0,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class DirInfo : public FH
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace endian
|
||||
{
|
||||
static
|
||||
|
||||
+3
-2
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
template<typename E>
|
||||
class Enum : public ToFromString
|
||||
{
|
||||
@@ -66,8 +67,8 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::string to_string() const;
|
||||
int from_string(const std::string &);
|
||||
std::string to_string() const final;
|
||||
int from_string(const std::string &) final;
|
||||
|
||||
public:
|
||||
int to_int() const
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class FH
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class FileInfo : public FH
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct fixed_mem_pool_t fixed_mem_pool_t;
|
||||
struct fixed_mem_pool_t
|
||||
|
||||
+14
-3
@@ -17,13 +17,14 @@
|
||||
*/
|
||||
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
namespace str
|
||||
{
|
||||
int
|
||||
@@ -50,7 +51,17 @@ namespace str
|
||||
from(const std::string &value_,
|
||||
int *int_)
|
||||
{
|
||||
*int_ = ::strtol(value_.c_str(),NULL,10);
|
||||
int tmp;
|
||||
char *endptr;
|
||||
|
||||
errno = 0;
|
||||
tmp = ::strtol(value_.c_str(),&endptr,10);
|
||||
if(errno != 0)
|
||||
return -EINVAL;
|
||||
if(endptr == value_.c_str())
|
||||
return -EINVAL;
|
||||
|
||||
*int_ = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,9 +18,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace str
|
||||
{
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@
|
||||
|
||||
const char POSIX_ACL_DEFAULT_XATTR[] = "system.posix_acl_default";
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
namespace acl
|
||||
@@ -31,7 +32,7 @@ namespace fs
|
||||
dir_has_defaults(const std::string &fullpath_)
|
||||
{
|
||||
int rv;
|
||||
std::string dirpath;
|
||||
std::string dirpath;
|
||||
|
||||
dirpath = fs::path::dirname(fullpath_);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
namespace acl
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
namespace attr
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
namespace attr
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
namespace attr
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "fs_fchmod.hpp"
|
||||
#include "fs_futimens.hpp"
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "errno.h"
|
||||
#include "fs_attr.hpp"
|
||||
#include "fs_clonepath.hpp"
|
||||
@@ -27,8 +25,11 @@
|
||||
#include "fs_xattr.hpp"
|
||||
#include "ugid.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
@@ -73,7 +74,7 @@ namespace fs
|
||||
return 0;
|
||||
|
||||
dirname = fs::path::dirname(relative_);
|
||||
if(!dirname.empty())
|
||||
if(dirname != "/")
|
||||
{
|
||||
rv = fs::clonepath(fromsrc_,tosrc_,dirname,return_metadata_errors_);
|
||||
if(rv == -1)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int clonepath(const std::string &from,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int64_t
|
||||
|
||||
@@ -22,13 +22,15 @@
|
||||
|
||||
#include "errno.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
|
||||
#include "errno.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
ssize_t
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#include "fs_copy_file_range.hpp"
|
||||
#include "fs_fstat.hpp"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
using std::vector;
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
int
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
namespace cow
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "fs_fstat.hpp"
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "fs_faccessat.hpp"
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user