mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-19 16:16:48 +00:00
Compare commits
154
Commits
2.28.3
...
read-failover2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d436cd231 | ||
|
|
562295be90 | ||
|
|
04844420dc | ||
|
|
9c352ac555 | ||
|
|
5a3184359d | ||
|
|
24f03b04eb | ||
|
|
610c75fa90 | ||
|
|
dbc4774e8e | ||
|
|
73e8ed72cd | ||
|
|
e694585561 | ||
|
|
0b1af2b8ef | ||
|
|
668ac4000a | ||
|
|
767039c492 | ||
|
|
b1c6d4864f | ||
|
|
fc3453932a | ||
|
|
98c49b8001 | ||
|
|
27bd39f5ac | ||
|
|
9f84f29e12 | ||
|
|
6311df774d | ||
|
|
9424784fad | ||
|
|
15fb751f65 | ||
|
|
3534fb1128 | ||
|
|
046844083f | ||
|
|
7ff995631e | ||
|
|
8675fce04a | ||
|
|
7fd629ebf3 | ||
|
|
2fe20b888e | ||
|
|
2696079601 | ||
|
|
ec15872a1f | ||
|
|
6cc6524997 | ||
|
|
4c711159df | ||
|
|
7e173108fd | ||
|
|
17925c4c93 | ||
|
|
90081f4e61 | ||
|
|
f488debf96 | ||
|
|
678626ef82 | ||
|
|
0bc6711a95 | ||
|
|
a925fbe59c | ||
|
|
dc1b698847 | ||
|
|
3c761b708b | ||
|
|
3bfdd78434 | ||
|
|
5f12fb6a5f | ||
|
|
f9b831eb1a | ||
|
|
da07bc9637 | ||
|
|
badf7e1c60 | ||
|
|
1f35b65725 | ||
|
|
3b382624e5 | ||
|
|
5f3a4cb0f1 | ||
|
|
0371b047f9 | ||
|
|
3f593d9c3b | ||
|
|
1fe32e9cd5 | ||
|
|
58a8f8326d | ||
|
|
30d13b7f77 | ||
|
|
124299e185 | ||
|
|
5989d41a86 | ||
|
|
c97102bc9d | ||
|
|
1d2a1ba31a | ||
|
|
159c2366c7 | ||
|
|
e639f3d375 | ||
|
|
23f870d8f2 | ||
|
|
173193df74 | ||
|
|
d1d21ead1f | ||
|
|
0709b2e447 | ||
|
|
50d967221e | ||
|
|
15a0aedd25 | ||
|
|
8052039c6f | ||
|
|
8afe72d371 | ||
|
|
0682ebf103 | ||
|
|
a93bd9f7fa | ||
|
|
6f76080c6b | ||
|
|
139e61efaa | ||
|
|
b50a128777 | ||
|
|
93218a343a | ||
|
|
5a0568495b | ||
|
|
3ec137c4ad | ||
|
|
d4eecf3b56 | ||
|
|
3a6738475a | ||
|
|
468d42088a | ||
|
|
4a4db4be84 | ||
|
|
08d267fc61 | ||
|
|
f2d4fbfdb2 | ||
|
|
c4a85f5fad | ||
|
|
c3f4d2914f | ||
|
|
c0990644e8 | ||
|
|
b1f30b703f | ||
|
|
62899565e6 | ||
|
|
ddb435a44b | ||
|
|
d699a97928 | ||
|
|
06660ca25e | ||
|
|
8ed3a1f36a | ||
|
|
8948fc2935 | ||
|
|
b4397f7f2e | ||
|
|
cb74573fc2 | ||
|
|
dbdd3e22fc | ||
|
|
4160a8e2f8 | ||
|
|
afb07b170d | ||
|
|
ebcf98ac2d | ||
|
|
54c41c4994 | ||
|
|
8d989777f8 | ||
|
|
a0c1c1a997 | ||
|
|
bed493e8dd | ||
|
|
add588fdea | ||
|
|
a61d420224 | ||
|
|
eb78c512ee | ||
|
|
ee5851bb1c | ||
|
|
c6bce81747 | ||
|
|
e0bb4e407a | ||
|
|
aad2257387 | ||
|
|
831dba3ef6 | ||
|
|
eff15c72a5 | ||
|
|
edb50e5411 | ||
|
|
7c37a69d25 | ||
|
|
be3499ee56 | ||
|
|
698c4147fc | ||
|
|
b5fae4b356 | ||
|
|
0888ee12a5 | ||
|
|
7e8635b24b | ||
|
|
8ba1abafec | ||
|
|
14d26ba434 | ||
|
|
3a46ec9fab | ||
|
|
ada90eab57 | ||
|
|
9b2634a1e5 | ||
|
|
4cade5685c | ||
|
|
62873d2d3b | ||
|
|
d119807adb | ||
|
|
10e8bd9fa3 | ||
|
|
5ce428ca59 | ||
|
|
2f769e75c4 | ||
|
|
a646fe016d | ||
|
|
f45a52d621 | ||
|
|
576ff3694e | ||
|
|
4cb866cee9 | ||
|
|
43ddff6ca9 | ||
|
|
bf4b39065b | ||
|
|
4b9f3de814 | ||
|
|
903d39f968 | ||
|
|
e6c8fa3fad | ||
|
|
9952c58a4a | ||
|
|
be8bb5d2a7 | ||
|
|
66f0d9e649 | ||
|
|
7b355b279d | ||
|
|
0f2f78c5b8 | ||
|
|
eb57425315 | ||
|
|
489ab23e89 | ||
|
|
4c55eb0cc0 | ||
|
|
389785265b | ||
|
|
a6717990a8 | ||
|
|
f4b8efce20 | ||
|
|
8cdb7174c4 | ||
|
|
4625a3cf43 | ||
|
|
55245e290a | ||
|
|
322fa5778a | ||
|
|
5eb3c8cbef | ||
|
|
5d827566f6 |
+234
@@ -0,0 +1,234 @@
|
||||
freebsd_task:
|
||||
name: "freebsd:12.1"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-1
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
#macos_task:
|
||||
# osx_instance:
|
||||
# image: catalina-base
|
||||
# script:
|
||||
# - tools/install-build-pkgs
|
||||
# - gmake -j4
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.11"
|
||||
container:
|
||||
image: alpine:3.11
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "centos:6"
|
||||
container:
|
||||
image: centos:6
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "centos:7"
|
||||
container:
|
||||
image: centos:7
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "centos:8"
|
||||
container:
|
||||
image: centos:8
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:30"
|
||||
container:
|
||||
image: fedora:30
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:31"
|
||||
container:
|
||||
image: fedora:31
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:32"
|
||||
container:
|
||||
image: fedora:32
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:14.04"
|
||||
container:
|
||||
image: ubuntu:14.04
|
||||
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:16.04"
|
||||
container:
|
||||
image: ubuntu:16.04
|
||||
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:18.04"
|
||||
container:
|
||||
image: ubuntu:18.04
|
||||
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:19.10"
|
||||
container:
|
||||
image: ubuntu:19.10
|
||||
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:20.04"
|
||||
container:
|
||||
image: ubuntu:20.04
|
||||
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:7"
|
||||
container:
|
||||
image: debian:8
|
||||
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:8"
|
||||
container:
|
||||
image: debian:8
|
||||
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:9"
|
||||
container:
|
||||
image: debian:9
|
||||
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:10"
|
||||
container:
|
||||
image: debian:10
|
||||
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
|
||||
@@ -0,0 +1,7 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [trapexit]
|
||||
patreon: trapexit
|
||||
open_collective: trapexit
|
||||
ko_fi: trapexit
|
||||
custom: ['https://paypal.me/trapexit','https://www.subscribestar.com/trapexit']
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: For the reporting of behavior not as described
|
||||
title: ''
|
||||
labels: bug, investigating
|
||||
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.
|
||||
|
||||
**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.)
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
|
||||
**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`
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature, investigating
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Question
|
||||
about: When you're looking for information
|
||||
title: ''
|
||||
labels: investigating, question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
You can also ask questions on Discord for realtime help: https://discord.gg/MpAr69V
|
||||
@@ -1,34 +0,0 @@
|
||||
General description
|
||||
===================
|
||||
|
||||
|
||||
|
||||
Expected behavior
|
||||
=================
|
||||
|
||||
|
||||
|
||||
Actual behavior
|
||||
===============
|
||||
|
||||
|
||||
|
||||
**Precise** steps to reproduce the behavior
|
||||
===========================================
|
||||
|
||||
> Explicitly list **all** steps to reproduce. Preferably create a minimal example of the problem using standard command line tools. The more variables (apps, settings, etc.) that are involved the more difficult it is to debug. Also, **please** be sure to have read all of the README. It contains a lot of information regarding known system and user issues.
|
||||
|
||||
|
||||
|
||||
System information
|
||||
==================
|
||||
Please provide as much of the following information as possible:
|
||||
|
||||
* [ ] mergerfs version: `mergerfs -V`
|
||||
* [ ] mergerfs settings: `cat /etc/fstab` or the command line arguments
|
||||
* [ ] Linux version: `uname -a`
|
||||
* [ ] Versions of any additional software being used
|
||||
* [ ] List of drives, filesystems, & sizes: `df -h`
|
||||
* [ ] strace of application having 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`
|
||||
|
||||
@@ -40,3 +40,5 @@ script:
|
||||
- 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) 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
|
||||
|
||||
@@ -37,13 +37,12 @@ endif
|
||||
endif
|
||||
|
||||
USE_XATTR = 1
|
||||
|
||||
FUSE_CFLAGS = -D_FILE_OFFSET_BITS=64 -Ilibfuse/include
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
DEBUG_FLAGS := -g
|
||||
OPT_FLAGS := -O0 -g
|
||||
else
|
||||
DEBUG_FLAGS :=
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
|
||||
ifeq ($(STATIC),1)
|
||||
@@ -58,29 +57,33 @@ else
|
||||
LTO_FLAGS :=
|
||||
endif
|
||||
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
OPTS = -O2
|
||||
SRC = $(wildcard src/*.cpp)
|
||||
OBJS = $(SRC:src/%.cpp=build/%.o)
|
||||
DEPS = $(SRC:src/%.cpp=build/%.d)
|
||||
MANPAGE = mergerfs.1
|
||||
CXXFLAGS += \
|
||||
$(OPTS) \
|
||||
$(DEBUG_FLAGS) \
|
||||
CXXFLAGS ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++0x \
|
||||
$(STATIC_FLAGS) \
|
||||
$(LTO_FLAGS) \
|
||||
-Wall \
|
||||
-Wno-unused-result \
|
||||
$(FUSE_CFLAGS) \
|
||||
-DFUSE_USE_VERSION=29 \
|
||||
-MMD \
|
||||
-Wno-unused-result \
|
||||
-MMD
|
||||
FUSE_FLAGS = \
|
||||
-Ilibfuse/include \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-DFUSE_USE_VERSION=29
|
||||
MFS_FLAGS = \
|
||||
-DUSE_XATTR=$(USE_XATTR) \
|
||||
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
|
||||
LDFLAGS += \
|
||||
-pthread \
|
||||
-lrt
|
||||
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-pthread \
|
||||
-lrt
|
||||
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
EXEC_PREFIX = $(PREFIX)
|
||||
DATAROOTDIR = $(PREFIX)/share
|
||||
@@ -108,7 +111,7 @@ objects: version build/stamp
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
build/mergerfs: libfuse objects
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
mergerfs: build/mergerfs
|
||||
|
||||
@@ -128,7 +131,7 @@ build/stamp:
|
||||
$(TOUCH) $@
|
||||
|
||||
build/%.o: src/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
.PHONY: clean
|
||||
clean: rpm-clean
|
||||
@@ -142,15 +145,15 @@ ifeq ($(GIT_REPO),1)
|
||||
endif
|
||||
|
||||
.PHONY: install
|
||||
install: install-base install-mount.mergerfs install-man
|
||||
install: install-base install-mount-tools install-man
|
||||
|
||||
install-base: build/mergerfs
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
$(INSTALL) -v -m 0755 build/mergerfs "$(INSTALLBINDIR)/mergerfs"
|
||||
|
||||
install-mount.mergerfs: install-base
|
||||
install-mount-tools: install-base
|
||||
$(MKDIR) -p "$(INSTALLBINDIR)"
|
||||
$(LN) -fs "mergerfs" "$(INSTALLBINDIR)/mount.mergerfs"
|
||||
$(MAKE) -C libfuse install
|
||||
|
||||
install-man: $(MANPAGE)
|
||||
$(MKDIR) -p "$(INSTALLMAN1DIR)"
|
||||
@@ -230,6 +233,6 @@ install-build-pkgs:
|
||||
|
||||
.PHONY: libfuse
|
||||
libfuse:
|
||||
$(MAKE) -C libfuse
|
||||
$(MAKE) DEBUG=$(DEBUG) -C libfuse
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
% mergerfs(1) mergerfs user manual
|
||||
% Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
% 2019-09-03
|
||||
% 2020-08-30
|
||||
|
||||
# NAME
|
||||
|
||||
@@ -34,7 +34,7 @@ mergerfs -o<options> <branches> <mountpoint>
|
||||
* supports POSIX ACLs
|
||||
|
||||
|
||||
# How it works
|
||||
# HOW IT WORKS
|
||||
|
||||
mergerfs logically merges multiple paths together. Think a union of sets. The file/s or directory/s acted on or presented through mergerfs are based on the policy chosen for that particular action. Read more about policies below.
|
||||
|
||||
@@ -62,24 +62,55 @@ A + B = C
|
||||
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.
|
||||
|
||||
|
||||
# TERMINOLOGY
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
|
||||
# BASIC SETUP
|
||||
|
||||
If you don't already know that you have a special use case then just start with one of the following option sets.
|
||||
|
||||
#### You don't need `mmap`
|
||||
|
||||
`use_ino,cache.files=off,dropcacheonclose=true,allow_other,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
|
||||
|
||||
### mount options
|
||||
|
||||
* **config**: Path to a config file. Same arguments as below in key=val 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)
|
||||
* **moveonenospc=BOOL**: When enabled if a **write** fails with **ENOSPC** or **EDQUOT** a scan of all drives will be done looking for the drive with the most free space which is at least the size of the file plus the amount which failed to write. An attempt to move the file to that drive will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false)
|
||||
* **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)
|
||||
* **nullrw=BOOL**: Turns reads and writes into no-ops. The request will succeed but do nothing. Useful for benchmarking mergerfs. (default: false)
|
||||
* **readfailover=BOOL**: Should a `read` fail search other branches for the same file and use that going forward. (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)
|
||||
* **xattr=passthrough|noattr|nosys**: Runtime control of xattrs. Default is to passthrough xattr requests. 'noattr' will short circuit as if nothing exists. 'nosys' will respond with ENOSYS as if xattrs are not supported or disabled. (default: passthrough)
|
||||
* **link_cow=BOOL**: When enabled if a regular file is opened which has a link count > 1 it will copy the file to a temporary file and rename over the original. Breaking the link and providing a basic copy-on-write function similar to cow-shell. (default: false)
|
||||
* **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)
|
||||
* **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)
|
||||
@@ -93,6 +124,7 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
|
||||
* **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)
|
||||
* **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)
|
||||
* **cache.readdir=BOOL**: Cache readdir (if supported by kernel) (default: false)
|
||||
* **direct_io**: deprecated - Bypass page cache. Use `cache.files=off` instead. (default: false)
|
||||
@@ -130,11 +162,11 @@ Each branch can have a suffix of `=RW` (read / write), `=RO` (read-only), or `=N
|
||||
|
||||
The above line will use all mount points in /mnt prefixed with **disk** and the **cdrom**.
|
||||
|
||||
To have the pool mounted at boot or otherwise accessable from related tools use **/etc/fstab**.
|
||||
To have the pool mounted at boot or otherwise accessible from related tools use **/etc/fstab**.
|
||||
|
||||
```
|
||||
# <file system> <mount point> <type> <options> <dump> <pass>
|
||||
/mnt/disk*:/mnt/cdrom /media/drives fuse.mergerfs allow_other,use_ino 0 0
|
||||
/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.
|
||||
@@ -142,18 +174,59 @@ To have the pool mounted at boot or otherwise accessable from related tools use
|
||||
**NOTE:** for mounting via **fstab** to work you must have **mount.fuse** installed. For Ubuntu/Debian it is included in the **fuse** package.
|
||||
|
||||
|
||||
### readfailover
|
||||
|
||||
One usecase of mergerfs is to create a union of mirrored files as a means to provide reliability. Often with networked filesysems such as NFS, sshfs, or rclone. This feature helps with situations where the device or mount of the file currently in use fails. If `read` returns `EIO` or `ENOTCONN` mergerfs will cycle through branches searching for the same relative path. It will `open` and attempt a `read`. Should that succeed the file will be used going forward. Otherwise the original error will be returned. Sometimes when an error occurs the `read` will still succeed but be a "short read." In those cases the kernel will issue a `fstat` to find out the length of the file. As a result this feature also performs a similar search and replacement behavior should `fstat` fail with the same errors.
|
||||
|
||||
|
||||
### inodecalc
|
||||
|
||||
Inodes (st_ino) are unique identifiers within a filesystem. Each mounted filesystem has device ID (st_dev) as well and together they can uniquely identify a file on the whole of the system. Entries on the same device with the same inode are in fact references to the same underlying file. It is a many to one relationship between names and an inode. Directories, however, do not have multiple links on most systems due to the complexity they add.
|
||||
|
||||
FUSE allows the server (mergerfs) to set inode values but not device IDs. Creating an inode value is somewhat complex in mergerfs' case as files aren't really in its control. If a policy changes what directory or file is to be selected or something changes out of band it becomes unclear what value should be used. Most software does not to care what the values are but those that do often break if a value changes unexpectedly. The tool `find` will abort a directory walk if it sees a directory inode change. NFS will return stale handle errors if the inode changes out of band. File dedup tools will usually leverage device ids and inodes as a shortcut in searching for duplicate files and would resort to full file comparisons should it find different inode values.
|
||||
|
||||
mergerfs offers multiple ways to calculate the inode in hopes of covering different usecases.
|
||||
|
||||
* passthrough: Passes through the underlying inode value. Mostly intended for testing as using this does not address any of the problems mentioned above and could confuse file deduplication software as inodes from different filesystems can be the same.
|
||||
* path-hash: Hashes the relative path of the entry in question. The underlying file's values are completely ignored. This means the inode value will always be the same for that file path. This is useful when using NFS and you make changes out of band such as copy data between branches. This also means that entries that do point to the same file will not be recognizable via inodes. That **does not** mean hard links don't work. They will.
|
||||
* path-hash32: 32bit version of path-hash.
|
||||
* devino-hash: Hashes the device id and inode of the underlying entry. This won't prevent issues with NFS should the policy pick a different file or files move out of band but will present the same inode for underlying files that do too.
|
||||
* devino-hash32: 32bit version of devino-hash.
|
||||
* hybrid-hash: Performs `path-hash` on directories and `devino-hash` on other file types. Since directories can't have hard links the static value won't make a difference and the files will get values useful for finding duplicates. Probably the best to use if not using NFS. As such it is the default.
|
||||
* hybrid-hash32: 32bit version of hybrid-hash.
|
||||
|
||||
32bit versions are provided as there is some software which does not handle 64bit inodes well.
|
||||
|
||||
While there is a risk of hash collision in tests of a couple million entries there were zero collisions. Unlike a typical filesystem FUSE filesystems can reuse inodes and not refer to the same entry. The internal identifier used to reference a file in FUSE is different from the inode value presented. The former is the `nodeid` and is actually a tuple of 2 64bit values: `nodeid` and `generation`. This tuple is not client facing. The inode that is presented to the client is passed through the kernel uninterpreted.
|
||||
|
||||
From FUSE docs regarding `use_ino`:
|
||||
|
||||
```
|
||||
Honor the st_ino field in the functions getattr() and
|
||||
fill_dir(). This value is used to fill in the st_ino field
|
||||
in the stat(2), lstat(2), fstat(2) functions and the d_ino
|
||||
field in the readdir(2) function. The filesystem does not
|
||||
have to guarantee uniqueness, however some applications
|
||||
rely on this value being unique for the whole filesystem.
|
||||
Note that this does *not* affect the inode that libfuse
|
||||
and the kernel use internally (also called the "nodeid").
|
||||
```
|
||||
|
||||
In the future the `use_ino` option will probably be removed as this feature should replace the original libfuse inode calculation strategy. Currently you still need to use `use_ino` in order to enable `inodecalc`.
|
||||
|
||||
|
||||
### fuse_msg_size
|
||||
|
||||
FUSE applications communicate with the kernel over a special character device: `/dev/fuse`. A large portion of the overhead associated with FUSE is the cost of going back and forth from user space and kernel space over that device. Generally speaking the fewer trips needed the better the performance will be. Reducing the number of trips can be done a number of ways. Kernel level caching and increasing message sizes being two significant ones. When it comes to reads and writes if the message size is doubled the number of trips are appoximately halved.
|
||||
FUSE applications communicate with the kernel over a special character device: `/dev/fuse`. A large portion of the overhead associated with FUSE is the cost of going back and forth from user space and kernel space over that device. Generally speaking the fewer trips needed the better the performance will be. Reducing the number of trips can be done a number of ways. Kernel level caching and increasing message sizes being two significant ones. When it comes to reads and writes if the message size is doubled the number of trips are approximately halved.
|
||||
|
||||
In Linux 4.20 a new feature was added allowing the negotiation of the max message size. Since the size is in multiples of [pages](https://en.wikipedia.org/wiki/Page_(computer_memory)) the feature is called `max_pages`. There is a maximum `max_pages` value of 256 (1MiB) and minimum of 1 (4KiB). The default used by Linux >=4.20, and hardcoded value used before 4.20, is 32 (128KiB). In mergerfs its referred to as `fuse_msg_size` to make it clear what it impacts and provide some abstraction.
|
||||
|
||||
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 `nullrw` section for benchmarking examples.
|
||||
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.
|
||||
|
||||
|
||||
### symlinkify
|
||||
|
||||
Due to the levels of indirection introduced by mergerfs and the underlying technology FUSE there can be varying levels of performance degredation. 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.
|
||||
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.
|
||||
|
||||
**WARNING:** The current implementation has a known issue in which if the file is open and being used when the file is converted to a symlink then the application which has that file open will receive an error when using it. This is unlikely to occur in practice but is something to keep in mind.
|
||||
|
||||
@@ -162,54 +235,41 @@ Due to the levels of indirection introduced by mergerfs and the underlying techn
|
||||
|
||||
### nullrw
|
||||
|
||||
Due to how FUSE works there is an overhead to all requests made to a FUSE filesystem. Meaning that even a simple passthrough will have some slowdown. However, generally the overhead is minimal in comparison to the cost of the underlying I/O. By disabling the underlying I/O we can test the theoretical performance boundries.
|
||||
Due to how FUSE works there is an overhead to all requests made to a FUSE filesystem that wouldn't exist for an in kernel one. Meaning that even a simple passthrough will have some slowdown. However, generally the overhead is minimal in comparison to the cost of the underlying I/O. By disabling the underlying I/O we can test the theoretical performance boundaries.
|
||||
|
||||
By enabling `nullrw` mergerfs will work as it always does **except** that all reads and writes will be no-ops. A write will succeed (the size of the write will be returned as if it were successful) but mergerfs does nothing with the data it was given. Similarly a read will return the size requested but won't touch the buffer.
|
||||
|
||||
Example:
|
||||
```
|
||||
$ dd if=/dev/zero of=/path/to/mergerfs/mount/benchmark ibs=1M obs=512 count=1024 iflag=dsync,nocache oflag=dsync,nocache conv=fdatasync status=progress
|
||||
1024+0 records in
|
||||
2097152+0 records out
|
||||
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 15.4067 s, 69.7 MB/s
|
||||
|
||||
$ dd if=/dev/zero of=/path/to/mergerfs/mount/benchmark ibs=1M obs=1M count=1024 iflag=dsync,nocache oflag=dsync,nocache conv=fdatasync status=progress
|
||||
1024+0 records in
|
||||
1024+0 records out
|
||||
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.219585 s, 4.9 GB/s
|
||||
|
||||
$ dd if=/path/to/mergerfs/mount/benchmark of=/dev/null bs=512 count=102400 iflag=dsync,nocache oflag=dsync,nocache conv=fdatasync status=progress
|
||||
102400+0 records in
|
||||
102400+0 records out
|
||||
52428800 bytes (52 MB, 50 MiB) copied, 0.757991 s, 69.2 MB/s
|
||||
|
||||
$ dd if=/path/to/mergerfs/mount/benchmark of=/dev/null bs=1M count=1024 iflag=dsync,nocache oflag=dsync,nocache conv=fdatasync status=progress
|
||||
1024+0 records in
|
||||
1024+0 records out
|
||||
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.18405 s, 5.8 GB/s
|
||||
```
|
||||
|
||||
It's important to test with different `obs` (output block size) values since the relative overhead is greater with smaller values. As you can see above the size of a read or write can massively impact theoretical performance. If an application performs much worse through mergerfs it could very well be that it doesn't optimally size its read and write requests. In such cases contact the mergerfs author so it can be investigated.
|
||||
See the BENCHMARKING section for suggestions on how to test.
|
||||
|
||||
|
||||
### xattr
|
||||
|
||||
Runtime extended attribute support can be managed via the `xattr` option. By default it will passthrough any xattr calls. Given xattr support is rarely used and can have significant performance implications mergerfs allows it to be disabled at runtime.
|
||||
Runtime extended attribute support can be managed via the `xattr` option. By default it will passthrough any xattr calls. Given xattr support is rarely used and can have significant performance implications mergerfs allows it to be disabled at runtime. The performance problems mostly comes when file caching is enabled. The kernel will send a `getxattr` for `security.capability` *before every single write*. It doesn't cache the responses to any `getxattr`. This might be addressed in the future but for now mergerfs can really only offer the following workarounds.
|
||||
|
||||
`noattr` will cause mergerfs to short circuit all xattr calls and return ENOATTR where appropriate. mergerfs still gets all the requests but they will not be forwarded on to the underlying filesystems. The runtime control will still function in this mode.
|
||||
|
||||
`nosys` will cause mergerfs to return ENOSYS for any xattr call. The difference with `noattr` is that the kernel will cache this fact and itself short circuit future calls. This will be more efficient than `noattr` but will cause mergerfs' runtime control via the hidden file to stop working.
|
||||
`nosys` will cause mergerfs to return ENOSYS for any xattr call. The difference with `noattr` is that the kernel will cache this fact and itself short circuit future calls. This is more efficient than `noattr` but will cause mergerfs' runtime control via the hidden file to stop working.
|
||||
|
||||
|
||||
### nfsopenhack
|
||||
|
||||
NFS is not fully POSIX compliant and historically certain behaviors, such as opening files with O_EXCL, are not or not well supported. When mergerfs (or any FUSE filesystem) is exported over NFS some of these issues come up due to how NFS and FUSE interact.
|
||||
|
||||
This hack addresses the issue where the creation of a file with a read-only mode but with a read/write or write only flag. Normally this is perfectly valid but NFS chops the one open call into multiple calls. Exactly how it is translated depends on the configuration and versions of the NFS server and clients but it results in a permission error because a normal user is not allowed to open a read-only file as writable.
|
||||
|
||||
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
|
||||
|
||||
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. 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 what file or 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 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
#### Function / Category classifications
|
||||
|
||||
#### Functions and their Category classifications
|
||||
|
||||
| Category | FUSE Functions |
|
||||
|----------|-------------------------------------------------------------------------------------|
|
||||
@@ -231,37 +291,50 @@ A path preserving policy will only consider drives where the relative path being
|
||||
|
||||
When using non-path preserving policies paths will be cloned to target drives as necessary.
|
||||
|
||||
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. The policy type defines the sorting but filtering is mostly uniform as described below.
|
||||
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** or **ENOSPC** depending on the reasons.
|
||||
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).
|
||||
|
||||
|
||||
| Policy | Description |
|
||||
|------------------|------------------------------------------------------------|
|
||||
| all | Search category: same as **epall**. Action category: same as **epall**. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
|
||||
| epall (existing path, all) | Search category: same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action category: apply to all found. Create category: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). |
|
||||
| all | Search: Same as **epall**. Action: Same as **epall**. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all branches. **create** works like **ff**. |
|
||||
| epall (existing path, all) | Search: Same as **epff** (but more expensive because it doesn't stop after finding a valid branch). Action: apply to all found. Create: for **mkdir**, **mknod**, and **symlink** it will apply to all found. **create** works like **epff** (but more expensive because it doesn't stop after finding a valid branch). |
|
||||
| epff (existing path, first found) | Given the order of the branches, as defined at mount time or configured at runtime, act on the first one found where the relative path exists. |
|
||||
| eplfs (existing path, least free space) | Of all the branches on which the relative path exists choose the drive with the least free space. |
|
||||
| eplus (existing path, least used space) | Of all the branches on which the relative path exists choose the drive with the least used space. |
|
||||
| epmfs (existing path, most free space) | Of all the branches on which the relative path exists choose the drive with the most free space. |
|
||||
| eppfrd (existing path, percentage free random distribution) | Like **pfrd** but limited to existing paths. |
|
||||
| eprand (existing path, random) | Calls **epall** and then randomizes. Returns 1. |
|
||||
| erofs | Exclusively return **-1** with **errno** set to **EROFS** (read-only filesystem). |
|
||||
| ff (first found) | Search category: same as **epff**. Action category: same as **epff**. Create category: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. |
|
||||
| lfs (least free space) | Search category: same as **eplfs**. Action category: same as **eplfs**. Create category: Pick the drive with the least available free space. |
|
||||
| lus (least used space) | Search category: same as **eplus**. Action category: same as **eplus**. Create category: Pick the drive with the least used space. |
|
||||
| mfs (most free space) | Search category: same as **epmfs**. Action category: same as **epmfs**. Create category: Pick the drive with the most available free space. |
|
||||
| ff (first found) | Search: Same as **epff**. Action: Same as **epff**. Create: Given the order of the drives, as defined at mount time or configured at runtime, act on the first one found. |
|
||||
| lfs (least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: Pick the drive with the least available free space. |
|
||||
| lus (least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: Pick the drive with the least used space. |
|
||||
| mfs (most free space) | Search: Same as **epmfs**. Action: Same as **epmfs**. Create: Pick the drive with the most available free space. |
|
||||
| msplfs (most shared path, least free space) | Search: Same as **eplfs**. Action: Same as **eplfs**. Create: like **eplfs** but walk back the path if it fails to find a branch at that level. |
|
||||
| msplus (most shared path, least used space) | Search: Same as **eplus**. Action: Same as **eplus**. Create: like **eplus** but walk back the path if it fails to find a branch at that level. |
|
||||
| mspmfs (most shared path, most free space) | Search: Same as **epmfs**. Action: Same as **epmfs**. Create: like **epmfs** but walk back the path if it fails to find a branch at that level. |
|
||||
| msppfrd (most shared path, percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Like **eppfrd** but will walk back the path if it fails to find a branch at that level. |
|
||||
| newest | Pick the file / directory with the largest mtime. |
|
||||
| pfrd (percentage free random distribution) | Search: Same as **eppfrd**. Action: Same as **eppfrd**. Create: Chooses a branch at random with the likelihood of selection based on a branch's available space relative to the total. |
|
||||
| rand (random) | Calls **all** and then randomizes. Returns 1. |
|
||||
|
||||
**NOTE:** If you are using an underlying filesystem that reserves blocks such as ext2, ext3, or ext4 be aware that mergerfs respects the reservation by using `f_bavail` (number of free blocks for unprivileged users) rather than `f_bfree` (number of free blocks) in policy calculations. **df** does NOT use `f_bavail`, it uses `f_bfree`, so direct comparisons between **df** output and mergerfs' policies is not appropriate.
|
||||
|
||||
|
||||
#### Defaults ####
|
||||
|
||||
@@ -292,18 +365,18 @@ The plan is to rewrite mergerfs to use the low level API so these invasive libfu
|
||||
|
||||
#### 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` be properly handled.
|
||||
**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.
|
||||
|
||||
`rename` and `link` are tricky functions in a union filesystem. `rename` only works within a single filesystem or device. If a rename can't be done atomically due to the source and destination paths existing on different mount points it will return **-1** with **errno = EXDEV** (cross device). So if a `rename`'s source and target are on different drives within the pool it creates an issue.
|
||||
`rename` and `link` are tricky functions in a union filesystem. `rename` only works within a single filesystem or device. If a rename can't be done atomically due to the source and destination paths existing on different mount points it will return **-1** with **errno = EXDEV** (cross device / improper link). So if a `rename`'s source and target are on different drives within the pool it creates an issue.
|
||||
|
||||
Originally mergerfs would return EXDEV whenever a rename was requested which was cross directory in any way. This made the code simple and was technically complient with POSIX requirements. However, many applications fail to handle EXDEV at all and treat it as a normal error or otherwise handle it poorly. Such apps include: gvfsd-fuse v1.20.3 and prior, Finder / CIFS/SMB client in Apple OSX 10.9+, NZBGet, Samba's recycling bin feature.
|
||||
Originally mergerfs would return EXDEV whenever a rename was requested which was cross directory in any way. This made the code simple and was technically compliant with POSIX requirements. However, many applications fail to handle EXDEV at all and treat it as a normal error or otherwise handle it poorly. Such apps include: gvfsd-fuse v1.20.3 and prior, Finder / CIFS/SMB client in Apple OSX 10.9+, NZBGet, Samba's recycling bin feature.
|
||||
|
||||
As a result a compromise was made in order to get most software to work while still obeying mergerfs' policies. Below is the basic logic.
|
||||
|
||||
* If using a **create** policy which tries to preserve directory paths (epff,eplfs,eplus,epmfs)
|
||||
* Using the **rename** policy get the list of files to rename
|
||||
* For each file attempt rename:
|
||||
* If failure with ENOENT run **create** policy
|
||||
* If failure with ENOENT (no such file or directory) run **create** policy
|
||||
* If create policy returns the same drive as currently evaluating then clone the path
|
||||
* Re-attempt rename
|
||||
* If **any** of the renames succeed the higher level rename is considered a success
|
||||
@@ -343,9 +416,10 @@ 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.
|
||||
|
||||
|
||||
# BUILDING
|
||||
# BUILD / UPDATE
|
||||
|
||||
**NOTE:** Prebuilt packages can be found at: https://github.com/trapexit/mergerfs/releases
|
||||
**NOTE:** Prebuilt packages can be found at and recommended for most users: https://github.com/trapexit/mergerfs/releases
|
||||
**NOTE:** Only tagged releases are supported. `master` and other branches should be considered works in progress.
|
||||
|
||||
First get the code from [github](https://github.com/trapexit/mergerfs).
|
||||
|
||||
@@ -363,13 +437,13 @@ $ make deb
|
||||
$ sudo dpkg -i ../mergerfs_version_arch.deb
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
#### RHEL / CentOS /Fedora
|
||||
```
|
||||
$ su -
|
||||
# cd mergerfs
|
||||
# tools/install-build-pkgs
|
||||
# make rpm
|
||||
# rpm -i rpmbuild/RPMS/<arch>/mergerfs-<verion>.<arch>.rpm
|
||||
# rpm -i rpmbuild/RPMS/<arch>/mergerfs-<version>.<arch>.rpm
|
||||
```
|
||||
|
||||
#### Generically
|
||||
@@ -396,7 +470,41 @@ make LTO=1 - build with link time optimization
|
||||
|
||||
# RUNTIME CONFIG
|
||||
|
||||
#### .mergerfs pseudo file ####
|
||||
#### 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
|
||||
|
||||
```
|
||||
<mountpoint>/.mergerfs
|
||||
@@ -409,7 +517,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 on read-only 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.
|
||||
|
||||
|
||||
##### Values #####
|
||||
@@ -545,12 +653,21 @@ If most files are read once through and closed (like media) it is best to enable
|
||||
|
||||
It is difficult to balance memory usage, cache bloat & duplication, and performance. Ideally mergerfs would be able to disable caching for the files it reads/writes but allow page caching for itself. That would limit the FUSE overhead. However, there isn't a good way to achieve this. It would need to open all files with O_DIRECT which places limitations on the what underlying filesystems would be supported and complicates the code.
|
||||
|
||||
kernel documenation: https://www.kernel.org/doc/Documentation/filesystems/fuse-io.txt
|
||||
kernel documentation: https://www.kernel.org/doc/Documentation/filesystems/fuse-io.txt
|
||||
|
||||
|
||||
#### entry & attribute caching
|
||||
|
||||
Given the relatively high cost of FUSE due to the kernel <-> userspace round trips there are kernel side caches for file entries and attributes. The entry cache limits the `lookup` calls to mergerfs which ask if a file exists. The attribute cache limits the need to make `getattr` calls to mergerfs which provide file attributes (mode, size, type, etc.). As with the page cache these should not be used if the underlying filesystems are being manipulated at the same time as it could lead to odd behavior or data corruption. The options for setting these are `cache.entry` and `cache.negative_entry` for the entry cache and `cache.attr` for the attributes cache. `cache.negative_entry` refers to the timeout for negative responses to lookups (non-existant files).
|
||||
Given the relatively high cost of FUSE due to the kernel <-> userspace round trips there are kernel side caches for file entries and attributes. The entry cache limits the `lookup` calls to mergerfs which ask if a file exists. The attribute cache limits the need to make `getattr` calls to mergerfs which provide file attributes (mode, size, type, etc.). As with the page cache these should not be used if the underlying filesystems are being manipulated at the same time as it could lead to odd behavior or data corruption. The options for setting these are `cache.entry` and `cache.negative_entry` for the entry cache and `cache.attr` for the attributes cache. `cache.negative_entry` refers to the timeout for negative responses to lookups (non-existent files).
|
||||
|
||||
|
||||
#### writeback caching
|
||||
|
||||
When `cache.files` is enabled the default is for it to perform writethrough caching. This behavior won't help improve performance as each write still goes one for one through the filesystem. By enabling the FUSE writeback cache small writes may be aggregated by the kernel and then sent to mergerfs as one larger request. This can greatly improve the throughput for apps which write to files inefficiently. The amount the kernel can aggregate is limited by the size of a FUSE message. Read the `fuse_msg_size` section for more details.
|
||||
|
||||
There is a small side effect as a result of enabling writeback caching. Underlying files won't ever be opened with O_APPEND or O_WRONLY. The former because the kernel then manages append mode and the latter because the kernel may request file data from mergerfs to populate the write cache. The O_APPEND change means that if a file is changed outside of mergerfs it could lead to corruption as the kernel won't know the end of the file has changed. That said any time you use caching you should keep from using the same file outside of mergerfs at the same time.
|
||||
|
||||
Note that if an application is properly sizing writes then writeback caching will have little or no effect. It will only help with writes of sizes below the FUSE message size (128K on older kernels, 1M on newer).
|
||||
|
||||
|
||||
#### policy caching
|
||||
@@ -576,12 +693,7 @@ As of version 4.20 Linux supports symlink caching. Significant performance incre
|
||||
|
||||
#### readdir caching
|
||||
|
||||
As of version 4.20 Linux supports readdir caching. This can have a significant impact on directory traversal. Especially when combined with entry (`cache.entry`) and attribute (`cache.attr`) caching. Setting `cache.readdir=true` will result in requesting readdir caching from the kernel on each `opendir`. If the kernel doesn't support readdir caching setting the option to `true` has no effect. This option is configuarable at runtime via xattr `user.mergerfs.cache.readdir`.
|
||||
|
||||
|
||||
#### writeback caching
|
||||
|
||||
writeback caching is a technique for improving write speeds by batching writes at a faster device and then bulk writing to the slower device. With FUSE the kernel will wait for a number of writes to be made and then send it to the filesystem as one request. mergerfs currently uses a modified and vendored libfuse 2.9.7 which does not support writeback caching. Adding said feature should not be difficult but benchmarking needs to be done to see if what effect it will have.
|
||||
As of version 4.20 Linux supports readdir caching. This can have a significant impact on directory traversal. Especially when combined with entry (`cache.entry`) and attribute (`cache.attr`) caching. Setting `cache.readdir=true` will result in requesting readdir caching from the kernel on each `opendir`. If the kernel doesn't support readdir caching setting the option to `true` has no effect. This option is configurable at runtime via xattr `user.mergerfs.cache.readdir`.
|
||||
|
||||
|
||||
#### tiered caching
|
||||
@@ -600,7 +712,7 @@ With #2 one could use dm-cache as well but there is another solution which requi
|
||||
1. Create 2 mergerfs pools. One which includes just the slow drives and one which has both the fast drives (SSD,NVME,etc.) and slow drives.
|
||||
2. The 'cache' pool should have the cache drives listed first.
|
||||
3. The best `create` policies to use for the 'cache' pool would probably be `ff`, `epff`, `lfs`, or `eplfs`. The latter two under the assumption that the cache drive(s) are far smaller than the backing drives. If using path preserving policies remember that you'll need to manually create the core directories of those paths you wish to be cached. Be sure the permissions are in sync. Use `mergerfs.fsck` to check / correct them. You could also tag the slow drives as `=NC` though that'd mean if the cache drives fill you'd get "out of space" errors.
|
||||
4. Enable `moveonenospc` and set `minfreespace` appropriately. Perhaps setting `minfreespace` to the size of the largest cache drive.
|
||||
4. Enable `moveonenospc` and set `minfreespace` appropriately. To make sure there is enough room on the "slow" pool you might want to set `minfreespace` to at least as large as the size of the largest cache drive if not larger. This way in the worst case the whole of the cache drive(s) can be moved to the other drives.
|
||||
5. Set your programs to use the cache pool.
|
||||
6. Save one of the below scripts or create you're own.
|
||||
7. Use `cron` (as root) to schedule the command at whatever frequency is appropriate for your workflow.
|
||||
@@ -656,21 +768,82 @@ done
|
||||
```
|
||||
|
||||
|
||||
# PERFORMANCE
|
||||
|
||||
mergerfs is at its core just a proxy and therefore its theoretical max performance is that of the underlying devices. However, given it is a FUSE filesystem working from userspace there is an increase in overhead relative to kernel based solutions. That said the performance can match the theoretical max but it depends greatly on the system's configuration. Especially when adding network filesystems into the mix there are many variables which can impact performance. Drive speeds and latency, network speeds and latency, general concurrency, read/write sizes, etc. Unfortunately, given the number of variables it has been difficult to find a single set of settings which provide optimal performance. If you're having performance issues please look over the suggestions below (including the benchmarking section.)
|
||||
|
||||
NOTE: be sure to read about these features before changing them to understand what behaviors it may impact
|
||||
|
||||
* enable (or disable) `splice_move`, `splice_read`, and `splice_write`
|
||||
* disable `security_capability` and/or `xattr`
|
||||
* increase cache timeouts `cache.attr`, `cache.entry`, `cache.negative_entry`
|
||||
* enable (or disable) page caching (`cache.files`)
|
||||
* enable `cache.writeback`
|
||||
* enable `cache.open`
|
||||
* enable `cache.statfs`
|
||||
* enable `cache.symlinks`
|
||||
* enable `cache.readdir`
|
||||
* change the number of worker threads
|
||||
* disable `posix_acl`
|
||||
* disable `async_read`
|
||||
* test theoretical performance using `nullrw` or mounting a ram disk
|
||||
* 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 you come across a setting that significantly impacts performance please contact trapexit so he may investigate further.
|
||||
|
||||
|
||||
# BENCHMARKING
|
||||
|
||||
Filesystems are complicated. They do many things and many of those are interconnected. Additionally, the OS, drivers, hardware, etc. all can impact performance. Therefore, when benchmarking, it is **necessary** that the test focus as narrowly as possible.
|
||||
|
||||
For most throughput is the key benchmark. To test throughput `dd` is useful but **must** be used with the correct settings in order to ensure the filesystem or device is actually being tested. The OS can and will cache data. Without forcing synchronous reads and writes and/or disabling caching the values returned will not be representative of the device's true performance.
|
||||
|
||||
When benchmarking through mergerfs ensure you only use 1 branch to remove any possibility of the policies complicating the situation. Benchmark the underlying filesystem first and then mount mergerfs over it and test again. If you're experience speeds below your expectation you will need to narrow down precisely which component is leading to the slowdown. Preferably test the following in the order listed (but not combined).
|
||||
|
||||
1. Enable `nullrw` mode with `nullrw=true`. This will effectively make reads and writes no-ops. Removing the underlying device / filesystem from the equation. This will give us the top theoretical speeds.
|
||||
2. Mount mergerfs over `tmpfs`. `tmpfs` is a RAM disk. Extremely high speed and very low latency. This is a more realistic best case scenario. Example: `mount -t tmpfs -o size=2G tmpfs /tmp/tmpfs`
|
||||
3. Mount mergerfs over a local drive. NVMe, SSD, HDD, etc. If you have more than one I'd suggest testing each of them as drives and/or controllers (their drivers) could impact performance.
|
||||
4. Finally, if you intend to use mergerfs with a network filesystem, either as the source of data or to combine with another through mergerfs, test each of those alone as above.
|
||||
|
||||
Once you find the component which has the performance issue you can do further testing with different options to see if they impact performance. For reads and writes the most relevant would be: `cache.files`, `async_read`, `splice_move`, `splice_read`, `splice_write`. Less likely but relevant when using NFS or with certain filesystems would be `security_capability`, `xattr`, and `posix_acl`. If you find a specific system, drive, filesystem, controller, etc. that performs poorly contact trapexit so he may investigate further.
|
||||
|
||||
Sometimes the problem is really the application accessing or writing data through mergerfs. Some software use small buffer sizes which can lead to more requests and therefore greater overhead. You can test this out yourself by replace `bs=1M` in the examples below with `ibs` or `obs` and using a size of `512` instead of `1M`. In one example test using `nullrw` the write speed dropped from 4.9GB/s to 69.7MB/s when moving from `1M` to `512`. Similar results were had when testing reads. Small writes overhead may be improved by leveraging a write cache but in casual tests little gain was found. More tests will need to be done before this feature would become available. If you have an app that appears slow with mergerfs it could be due to this. Contact trapexit so he may investigate further.
|
||||
|
||||
|
||||
### write benchmark
|
||||
|
||||
```
|
||||
$ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=fdatasync status=progress
|
||||
```
|
||||
|
||||
### read benchmark
|
||||
|
||||
```
|
||||
$ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress
|
||||
```
|
||||
|
||||
|
||||
# TIPS / NOTES
|
||||
|
||||
* **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.
|
||||
* Do **not** use `cache.files=off` or `direct_io` if you expect applications (such as rtorrent) to [mmap](http://linux.die.net/man/2/mmap) files. Shared mmap is not currently supported in FUSE w/ `direct_io` enabled. Enabling `dropcacheonclose` is recommended when `cache.files=partial|full|auto-full` or `direct_io=false`.
|
||||
* 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** its 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.
|
||||
* [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.
|
||||
|
||||
|
||||
# KNOWN ISSUES / BUGS
|
||||
|
||||
#### kernel issues & bugs
|
||||
|
||||
[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.
|
||||
@@ -680,11 +853,11 @@ The reason this is the default is because any other policy would be more expensi
|
||||
If you always want the directory information from the one with the most recent mtime then use the `newest` policy for `getattr`.
|
||||
|
||||
|
||||
#### `mv /mnt/pool/foo /mnt/disk1/foo` removes `foo`
|
||||
#### 'mv /mnt/pool/foo /mnt/disk1/foo' removes 'foo'
|
||||
|
||||
This is not a bug.
|
||||
|
||||
Run in verbose mode to better undertand what's happening:
|
||||
Run in verbose mode to better understand what's happening:
|
||||
|
||||
```
|
||||
$ mv -v /mnt/pool/foo /mnt/disk1/foo
|
||||
@@ -701,43 +874,30 @@ If you want to move files to one drive just copy them there and use mergerfs.ded
|
||||
|
||||
#### cached memory appears greater than it should be
|
||||
|
||||
Use `cache.files=off` or `direct_io=true`. See the section on page caching.
|
||||
Use `cache.files=off` and/or `dropcacheonclose=true`. See the section on page caching.
|
||||
|
||||
|
||||
#### NFS clients returning ESTALE / Stale file handle
|
||||
|
||||
Be sure to use `noforget` and `use_ino` arguments.
|
||||
NFS does not like out of band changes. That is especially true of inode values.
|
||||
|
||||
Be sure to use the following options:
|
||||
|
||||
#### NFS clients don't work
|
||||
|
||||
Some NFS clients appear to fail when a mergerfs mount is exported. Kodi in particular seems to have issues.
|
||||
|
||||
Try enabling the `use_ino` option. Some have reported that it fixes the issue.
|
||||
* noforget
|
||||
* use_ino
|
||||
* inodecalc=path-hash
|
||||
|
||||
|
||||
#### rtorrent fails with ENODEV (No such device)
|
||||
|
||||
Be sure to set `cache.files=partial|full|auto-full` or turn off `direct_io`. rtorrent and some other applications use [mmap](http://linux.die.net/man/2/mmap) to read and write to files and offer no failback to traditional methods. FUSE does not currently support mmap while using `direct_io`. There may be a performance penalty on writes with `direct_io` off as well as the problem of double caching but it's the only way to get such applications to work. If the performance loss is too high for other apps you can mount mergerfs twice. Once with `direct_io` enabled and one without it. Be sure to set `dropcacheonclose=true` if not using `direct_io`.
|
||||
|
||||
|
||||
#### rtorrent fails with files >= 4GiB
|
||||
|
||||
This is a kernel bug with mmap and FUSE on 32bit platforms. A fix should become available for all LTS releases.
|
||||
|
||||
https://marc.info/?l=linux-fsdevel&m=155550785230874&w=2
|
||||
Be sure to set `cache.files=partial|full|auto-full` or turn off `direct_io`. rtorrent and some other applications use [mmap](http://linux.die.net/man/2/mmap) to read and write to files and offer no fallback to traditional methods. FUSE does not currently support mmap while using `direct_io`. There may be a performance penalty on writes with `direct_io` off as well as the problem of double caching but it's the only way to get such applications to work. If the performance loss is too high for other apps you can mount mergerfs twice. Once with `direct_io` enabled and one without it. Be sure to set `dropcacheonclose=true` if not using `direct_io`.
|
||||
|
||||
|
||||
#### Plex doesn't work with mergerfs
|
||||
|
||||
It does. If you're trying to put Plex's config / metadata on mergerfs you have to leave `direct_io` off because Plex is using sqlite which apparently needs mmap. mmap doesn't work with `direct_io`. To fix this place the data elsewhere or disable `direct_io` (with `dropcacheonclose=true`).
|
||||
It does. If you're trying to put Plex's config / metadata / database on mergerfs you can't set `cache.files=off` because Plex is using sqlite3 with mmap enabled. Shared mmap is not supported by Linux's FUSE implementation when page caching is disabled. To fix this place the data elsewhere (preferable) or enable `cache.files` (with `dropcacheonclose=true`). Sqlite3 does not need mmap but the developer needs to fall back to standard IO if mmap fails.
|
||||
|
||||
If the issue is that scanning doesn't seem to pick up media then be sure to set `func.getattr=newest` as mentioned above.
|
||||
|
||||
|
||||
#### mmap performance is really bad
|
||||
|
||||
There [is a bug](https://lkml.org/lkml/2016/3/16/260) in caching which affects overall performance of mmap through FUSE in Linux 4.x kernels. It is fixed in [4.4.10 and 4.5.4](https://lkml.org/lkml/2016/5/11/59).
|
||||
If the issue is that scanning doesn't seem to pick up media then be sure to set `func.getattr=newest` though generally a full scan will pick up all media anyway.
|
||||
|
||||
|
||||
#### When a program tries to move or rename a file it fails
|
||||
@@ -749,6 +909,11 @@ The problem is that many applications do not properly handle `EXDEV` errors whic
|
||||
Ideally the offending software would be fixed and it is recommended that if you run into this problem you contact the software's author and request proper handling of `EXDEV` errors.
|
||||
|
||||
|
||||
#### my 32bit software has problems
|
||||
|
||||
Some software have problems with 64bit inode values. The symptoms can include EOVERFLOW errors when trying to list files. You can address this by setting `inodecalc` to one of the 32bit based algos as described in the relevant section.
|
||||
|
||||
|
||||
#### Samba: Moving files / directories fails
|
||||
|
||||
Workaround: Copy the file/directory and then remove the original rather than move.
|
||||
@@ -762,7 +927,7 @@ In Apple's MacOSX 10.9 they replaced Samba (client and server) with their own pr
|
||||
|
||||
#### Trashing files occasionally fails
|
||||
|
||||
This is the same issue as with Samba. `rename` returns `EXDEV` (in our case that will really only happen with path preserving policies like `epmfs`) and the software doesn't handle the situtation well. This is unfortunately a common failure of software which moves files around. The standard indicates that an implementation `MAY` choose to support non-user home directory trashing of files (which is a `MUST`). The implementation `MAY` also support "top directory trashes" which many probably do.
|
||||
This is the same issue as with Samba. `rename` returns `EXDEV` (in our case that will really only happen with path preserving policies like `epmfs`) and the software doesn't handle the situation well. This is unfortunately a common failure of software which moves files around. The standard indicates that an implementation `MAY` choose to support non-user home directory trashing of files (which is a `MUST`). The implementation `MAY` also support "top directory trashes" which many probably do.
|
||||
|
||||
To create a `$topdir/.Trash` directory as defined in the standard use the [mergerfs-tools](https://github.com/trapexit/mergerfs-tools) tool `mergerfs.mktrash`.
|
||||
|
||||
@@ -775,79 +940,43 @@ Make sure to use the `use_ino` option.
|
||||
|
||||
Due to the overhead of [getgroups/setgroups](http://linux.die.net/man/2/setgroups) mergerfs utilizes a cache. This cache is opportunistic and per thread. Each thread will query the supplemental groups for a user when that particular thread needs to change credentials and will keep that data for the lifetime of the thread. This means that if a user is added to a group it may not be picked up without the restart of mergerfs. However, since the high level FUSE API's (at least the standard version) thread pool dynamically grows and shrinks it's possible that over time a thread will be killed and later a new thread with no cache will start and query the new data.
|
||||
|
||||
The gid cache uses fixed storage to simplify the design and be compatible with older systems which may not have C++11 compilers. There is enough storage for 256 users' supplemental groups. Each user is allowed upto 32 supplemental groups. Linux >= 2.6.3 allows upto 65535 groups per user but most other *nixs allow far less. NFS allowing only 16. The system does handle overflow gracefully. If the user has more than 32 supplemental groups only the first 32 will be used. If more than 256 users are using the system when an uncached user is found it will evict an existing user's cache at random. So long as there aren't more than 256 active users this should be fine. If either value is too low for your needs you will have to modify `gidcache.hpp` to increase the values. Note that doing so will increase the memory needed by each thread.
|
||||
The gid cache uses fixed storage to simplify the design and be compatible with older systems which may not have C++11 compilers. There is enough storage for 256 users' supplemental groups. Each user is allowed up to 32 supplemental groups. Linux >= 2.6.3 allows up to 65535 groups per user but most other *nixs allow far less. NFS allowing only 16. The system does handle overflow gracefully. If the user has more than 32 supplemental groups only the first 32 will be used. If more than 256 users are using the system when an uncached user is found it will evict an existing user's cache at random. So long as there aren't more than 256 active users this should be fine. If either value is too low for your needs you will have to modify `gidcache.hpp` to increase the values. Note that doing so will increase the memory needed by each thread.
|
||||
|
||||
While not a bug some users have found when using containers that supplemental groups defined inside the container don't work properly with regard to permissions. This is expected as mergerfs lives outside the container and therefore is querying the host's group database. There might be a hack to work around this (make mergerfs read the /etc/group file in the container) but it is not yet implemented and would be limited to Linux and the /etc/group DB. Preferably users would mount in the host group file into the containers or use a standard shared user & groups technology like NIS or LDAP.
|
||||
|
||||
|
||||
#### mergerfs or libfuse crashing
|
||||
|
||||
**NOTE:** as of mergerfs 2.22.0 it includes the most recent version of libfuse (or requires libfuse-2.9.7) so any crash should be reported. For older releases continue reading...
|
||||
First... always upgrade to the latest version unless told otherwise.
|
||||
|
||||
If using mergerfs below 2.22.0:
|
||||
|
||||
If suddenly the mergerfs mount point disappears and `Transport endpoint is not connected` is returned when attempting to perform actions within the mount directory **and** the version of libfuse (use `mergerfs -v` to find the version) is older than `2.9.4` its likely due to a bug in libfuse. Affected versions of libfuse can be found in Debian Wheezy, Ubuntu Precise and others.
|
||||
|
||||
In order to fix this please install newer versions of libfuse. If using a Debian based distro (Debian,Ubuntu,Mint) you can likely just install newer versions of [libfuse](https://packages.debian.org/unstable/libfuse2) and [fuse](https://packages.debian.org/unstable/fuse) from the repo of a newer release.
|
||||
|
||||
If using mergerfs at or above 2.22.0:
|
||||
|
||||
First upgrade if possible, check the known bugs section, and contact trapexit.
|
||||
|
||||
|
||||
#### mergerfs appears to be crashing or exiting
|
||||
|
||||
There seems to be an issue with Linux version `4.9.0` and above in which an invalid message appears to be transmitted to libfuse (used by mergerfs) causing it to exit. No messages will be printed in any logs as its not a proper crash. Debugging of the issue is still ongoing and can be followed via the [fuse-devel thread](https://sourceforge.net/p/fuse/mailman/message/35662577).
|
||||
|
||||
#### mergerfs under heavy load and memory preasure leads to kernel panic
|
||||
|
||||
https://lkml.org/lkml/2016/9/14/527
|
||||
|
||||
```
|
||||
[25192.515454] kernel BUG at /build/linux-a2WvEb/linux-4.4.0/mm/workingset.c:346!
|
||||
[25192.517521] invalid opcode: 0000 [#1] SMP
|
||||
[25192.519602] Modules linked in: netconsole ip6t_REJECT nf_reject_ipv6 ipt_REJECT nf_reject_ipv4 configfs binfmt_misc veth bridge stp llc nf_conntrack_ipv6 nf_defrag_ipv6 xt_conntrack ip6table_filter ip6_tables xt_multiport iptable_filter ipt_MASQUERADE nf_nat_masquerade_ipv4 xt_comment xt_nat iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack xt_CHECKSUM xt_tcpudp iptable_mangle ip_tables x_tables intel_rapl x86_pkg_temp_thermal intel_powerclamp eeepc_wmi asus_wmi coretemp sparse_keymap kvm_intel ppdev kvm irqbypass mei_me 8250_fintek input_leds serio_raw parport_pc tpm_infineon mei shpchp mac_hid parport lpc_ich autofs4 drbg ansi_cprng dm_crypt algif_skcipher af_alg btrfs raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid0 multipath linear raid10 raid1 i915 crct10dif_pclmul crc32_pclmul aesni_intel i2c_algo_bit aes_x86_64 drm_kms_helper lrw gf128mul glue_helper ablk_helper syscopyarea cryptd sysfillrect sysimgblt fb_sys_fops drm ahci r8169 libahci mii wmi fjes video [last unloaded: netconsole]
|
||||
[25192.540910] CPU: 2 PID: 63 Comm: kswapd0 Not tainted 4.4.0-36-generic #55-Ubuntu
|
||||
[25192.543411] Hardware name: System manufacturer System Product Name/P8H67-M PRO, BIOS 3904 04/27/2013
|
||||
[25192.545840] task: ffff88040cae6040 ti: ffff880407488000 task.ti: ffff880407488000
|
||||
[25192.548277] RIP: 0010:[<ffffffff811ba501>] [<ffffffff811ba501>] shadow_lru_isolate+0x181/0x190
|
||||
[25192.550706] RSP: 0018:ffff88040748bbe0 EFLAGS: 00010002
|
||||
[25192.553127] RAX: 0000000000001c81 RBX: ffff8802f91ee928 RCX: ffff8802f91eeb38
|
||||
[25192.555544] RDX: ffff8802f91ee938 RSI: ffff8802f91ee928 RDI: ffff8804099ba2c0
|
||||
[25192.557914] RBP: ffff88040748bc08 R08: 000000000001a7b6 R09: 000000000000003f
|
||||
[25192.560237] R10: 000000000001a750 R11: 0000000000000000 R12: ffff8804099ba2c0
|
||||
[25192.562512] R13: ffff8803157e9680 R14: ffff8803157e9668 R15: ffff8804099ba2c8
|
||||
[25192.564724] FS: 0000000000000000(0000) GS:ffff88041f280000(0000) knlGS:0000000000000000
|
||||
[25192.566990] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
|
||||
[25192.569201] CR2: 00007ffabb690000 CR3: 0000000001e0a000 CR4: 00000000000406e0
|
||||
[25192.571419] Stack:
|
||||
[25192.573550] ffff8804099ba2c0 ffff88039e4f86f0 ffff8802f91ee928 ffff8804099ba2c8
|
||||
[25192.575695] ffff88040748bd08 ffff88040748bc58 ffffffff811b99bf 0000000000000052
|
||||
[25192.577814] 0000000000000000 ffffffff811ba380 000000000000008a 0000000000000080
|
||||
[25192.579947] Call Trace:
|
||||
[25192.582022] [<ffffffff811b99bf>] __list_lru_walk_one.isra.3+0x8f/0x130
|
||||
[25192.584137] [<ffffffff811ba380>] ? memcg_drain_all_list_lrus+0x190/0x190
|
||||
[25192.586165] [<ffffffff811b9a83>] list_lru_walk_one+0x23/0x30
|
||||
[25192.588145] [<ffffffff811ba544>] scan_shadow_nodes+0x34/0x50
|
||||
[25192.590074] [<ffffffff811a0e9d>] shrink_slab.part.40+0x1ed/0x3d0
|
||||
[25192.591985] [<ffffffff811a53da>] shrink_zone+0x2ca/0x2e0
|
||||
[25192.593863] [<ffffffff811a64ce>] kswapd+0x51e/0x990
|
||||
[25192.595737] [<ffffffff811a5fb0>] ? mem_cgroup_shrink_node_zone+0x1c0/0x1c0
|
||||
[25192.597613] [<ffffffff810a0808>] kthread+0xd8/0xf0
|
||||
[25192.599495] [<ffffffff810a0730>] ? kthread_create_on_node+0x1e0/0x1e0
|
||||
[25192.601335] [<ffffffff8182e34f>] ret_from_fork+0x3f/0x70
|
||||
[25192.603193] [<ffffffff810a0730>] ? kthread_create_on_node+0x1e0/0x1e0
|
||||
```
|
||||
|
||||
There is a bug in the kernel. A work around appears to be turning off `splice`. Don't add the `splice_*` arguments or add `no_splice_write,no_splice_move,no_splice_read`. This, however, is not guaranteed to work.
|
||||
There seems to be an issue with Linux version `4.9.0` and above in which an invalid message appears to be transmitted to libfuse (used by mergerfs) causing it to exit. No messages will be printed in any logs as it's not a proper crash. Debugging of the issue is still ongoing and can be followed via the [fuse-devel thread](https://sourceforge.net/p/fuse/mailman/message/35662577).
|
||||
|
||||
|
||||
#### rm: fts_read failed: No such file or directory
|
||||
|
||||
NOTE: This is only relevant to mergerfs versions at or below v2.25.x and should not occur in more recent versions. See the notes on `unlink`.
|
||||
|
||||
Not *really* a bug. The FUSE library will move files when asked to delete them as a way to deal with certain edge cases and then later delete that file when its clear the file is no longer needed. This however can lead to two issues. One is that these hidden files are noticed by `rm -rf` or `find` when scanning directories and they may try to remove them and they might have disappeared already. There is nothing *wrong* about this happening but it can be annoying. The second issue is that a directory might not be able to removed on account of the hidden file being still there.
|
||||
|
||||
Using the **hard_remove** option will make it so these temporary files are not used and files are deleted immedately. That has a side effect however. Files which are unlinked and then they are still used (in certain forms) will result in an error (ENOENT).
|
||||
Please update. This is only happened to mergerfs versions at or below v2.25.x and will not occur in more recent versions.
|
||||
|
||||
|
||||
# FAQ
|
||||
|
||||
#### How well does mergerfs scale? Is it "production ready?"
|
||||
|
||||
Users have reported running mergerfs on everything from a Raspberry Pi to dual socket Xeon systems with >20 cores. I'm aware of at least a few companies which use mergerfs in production. [Open Media Vault](https://www.openmediavault.org) includes mergerfs as its sole solution for pooling drives.
|
||||
Users have reported running mergerfs on everything from a Raspberry Pi to dual socket Xeon systems with >20 cores. I'm aware of at least a few companies which use mergerfs in production. [Open Media Vault](https://www.openmediavault.org) includes mergerfs as its sole solution for pooling drives. The author of mergerfs had it running for over 300 days managing 16+ drives with reasonably heavy 24/7 read and write usage. Stopping only after the machine's power supply died.
|
||||
|
||||
Most serious issues (crashes or data corruption) have been due to [kernel bugs](https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs). All of which are fixed in stable releases.
|
||||
|
||||
|
||||
#### Can mergerfs be used with drives which already have data / are in use?
|
||||
@@ -862,9 +991,38 @@ MergerFS is **not** a traditional filesystem. MergerFS is **not** RAID. It does
|
||||
See the previous question's answer.
|
||||
|
||||
|
||||
#### What policies should I use?
|
||||
|
||||
Unless you're doing something more niche the average user is probably best off using `mfs` for `category.create`. It will spread files out across your branches based on available space. Use `mspmfs` if you want to try to colocate the data a bit more. You may want to use `lus` if you prefer a slightly different distribution of data if you have a mix of smaller and larger drives. Generally though `mfs`, `lus`, or even `rand` are good for the general use case. If you are starting with an imbalanced pool you can use the tool **mergerfs.balance** to redistribute files across the pool.
|
||||
|
||||
If you really wish to try to colocate files based on directory you can set `func.create` to `epmfs` or similar and `func.mkdir` to `rand` or `eprand` depending on if you just want to colocate generally or on specific branches. Either way the *need* to colocate is rare. For instance: if you wish to remove the drive regularly and want the data to predictably be on that drive or if you don't use backup at all and don't wish to replace that data piecemeal. In which case using path preservation can help but will require some manual attention. Colocating after the fact can be accomplished using the **mergerfs.consolidate** tool. If you don't need strict colocation which the `ep` policies provide then you can use the `msp` based policies which will walk back the path till finding a branch that works.
|
||||
|
||||
Ultimately there is no correct answer. It is a preference or based on some particular need. mergerfs is very easy to test and experiment with. I suggest creating a test setup and experimenting to get a sense of what you want.
|
||||
|
||||
The reason `mfs` is not the default `category.create` policy is historical. When/if a 3.X gets released it will be changed to minimize confusion people often have with path preserving policies.
|
||||
|
||||
|
||||
#### What settings should I use?
|
||||
|
||||
Depends on what features you want. Generally speaking there are no "wrong" settings. All settings are performance or feature related. The best bet is to read over the available options and choose what fits your situation. If something isn't clear from the documentation please reach out and the documentation will be improved.
|
||||
|
||||
That said, for the average person, the following should be fine:
|
||||
|
||||
`-o use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs`
|
||||
|
||||
|
||||
#### Why are all my files ending up on 1 drive?!
|
||||
|
||||
Did you start with empty drives? Did you explicitly configure a `category.create` policy? Are you using an `existing path` / `path preserving` policy?
|
||||
|
||||
The default create policy is `epmfs`. That is a path preserving algorithm. With such a policy for `mkdir` and `create` with a set of empty drives it will select only 1 drive when the first directory is created. Anything, files or directories, created in that first directory will be placed on the same branch because it is preserving paths.
|
||||
|
||||
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?
|
||||
|
||||
Yes. You need to use `use_ino` to support proper reporting of inodes.
|
||||
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.
|
||||
|
||||
@@ -876,23 +1034,23 @@ Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs or aufs
|
||||
|
||||
#### Why can't I see my files / directories?
|
||||
|
||||
It's almost always a permissions issue. Unlike mhddfs, which runs as root and attempts to access content as such, mergerfs always changes its credentials to that of the caller. This means that if the user does not have access to a file or directory than neither will mergerfs. However, because mergerfs is creating a union of paths it may be able to read some files and directories on one drive but not another resulting in an incomplete set.
|
||||
It's almost always a permissions issue. Unlike mhddfs and unionfs-fuse, which runs as root and attempts to access content as such, mergerfs always changes its credentials to that of the caller. This means that if the user does not have access to a file or directory than neither will mergerfs. However, because mergerfs is creating a union of paths it may be able to read some files and directories on one drive but not another resulting in an incomplete set.
|
||||
|
||||
Whenever you run into a split permission issue (seeing some but not all files) try using [mergerfs.fsck](https://github.com/trapexit/mergerfs-tools) tool to check for and fix the mismatch. If you aren't seeing anything at all be sure that the basic permissions are correct. The user and group values are correct and that directories have their executable bit set. A common mistake by users new to Linux is to `chmod -R 644` when they should have `chmod -R u=rwX,go=rX`.
|
||||
|
||||
If using a network filesystem such as NFS, SMB, CIFS (Samba) be sure to pay close attention to anything regarding permissioning and users. Root squashing and user translation for instance has bitten a few mergerfs users. Some of these also affect the use of mergerfs from container platforms such as Docker.
|
||||
|
||||
|
||||
#### Why is only one drive being used?
|
||||
#### Is my OS's libfuse needed for mergerfs to work?
|
||||
|
||||
Are you using a path preserving policy? The default policy for file creation is `epmfs`. That means only the drives with the path preexisting will be considered when creating a file. If you don't care about where files and directories are created you likely shouldn't be using a path preserving policy and instead something like `mfs`.
|
||||
No. Normally `mount.fuse` is needed to get mergerfs (or any FUSE filesystem to mount using the `mount` command but in vendoring the libfuse library the `mount.fuse` app has been renamed to `mount.mergerfs` meaning the filesystem type in `fstab` can simply be `mergerfs`. That said there should be no harm in having it installed and continuing to using `fuse.mergerfs` as the type in `/etc/fstab`.
|
||||
|
||||
This can be especially apparent when filling an empty pool from an external source. 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 transfering your data. Setting `func.mkdir=epall` can simplify managing path perservation for `create`.
|
||||
If `mergerfs` doesn't work as a type it could be due to how the `mount.mergerfs` tool was installed. Must be in `/sbin/` with proper permissions.
|
||||
|
||||
|
||||
#### Why was libfuse embedded into mergerfs?
|
||||
|
||||
1. A significant number of users use mergerfs on distros with old versions of libfuse which have serious bugs. Requiring updated versions of libfuse on those distros isn't pratical (no package offered, user inexperience, etc.). The only practical way to provide a stable runtime on those systems was to "vendor" / embed the library into the project.
|
||||
1. A significant number of users use mergerfs on distros with old versions of libfuse which have serious bugs. Requiring updated versions of libfuse on those distros isn't practical (no package offered, user inexperience, etc.). The only practical way to provide a stable runtime on those systems was to "vendor" / embed the library into the project.
|
||||
2. mergerfs was written to use the high level API. There are a number of limitations in the HLAPI that make certain features difficult or impossible to implement. While some of these features could be patched into newer versions of libfuse without breaking the public API some of them would require hacky code to provide backwards compatibility. While it may still be worth working with upstream to address these issues in future versions, since the library needs to be vendored for stability and compatibility reasons it is preferable / easier to modify the API. Longer term the plan is to rewrite mergerfs to use the low level API.
|
||||
|
||||
|
||||
@@ -926,21 +1084,42 @@ While aufs can offer better peak performance mergerfs provides more configurabil
|
||||
UnionFS is more like aufs than mergerfs in that it offers overlay / CoW features. If you're just looking to create a union of drives and want flexibility in file/directory placement then mergerfs offers that whereas unionfs is more for overlaying RW filesystems over RO ones.
|
||||
|
||||
|
||||
#### Why use mergerfs over overlayfs?
|
||||
|
||||
Same reasons as with unionfs.
|
||||
|
||||
|
||||
#### Why use mergerfs over LVM/ZFS/BTRFS/RAID0 drive concatenation / striping?
|
||||
|
||||
With simple JBOD / drive concatenation / stripping / RAID0 a single drive failure will result in full pool failure. mergerfs performs a similar behavior without the possibility of catastrophic failure and the difficulties in recovery. Drives may fail however all other data will continue to be accessable.
|
||||
With simple JBOD / drive concatenation / stripping / RAID0 a single drive failure will result in full pool failure. mergerfs performs a similar function without the possibility of catastrophic failure and the difficulties in recovery. Drives may fail, however, all other data will continue to be accessible.
|
||||
|
||||
When combined with something like [SnapRaid](http://www.snapraid.it) and/or an offsite backup solution you can have the flexibilty of JBOD without the single point of failure.
|
||||
When combined with something like [SnapRaid](http://www.snapraid.it) and/or an offsite backup solution you can have the flexibility of JBOD without the single point of failure.
|
||||
|
||||
|
||||
#### Why use mergerfs over ZFS?
|
||||
|
||||
MergerFS is not intended to be a replacement for ZFS. MergerFS is intended to provide flexible pooling of arbitrary drives (local or remote), of arbitrary sizes, and arbitrary filesystems. For `write once, read many` usecases such as bulk media storage. Where data integrity and backup is managed in other ways. In that situation ZFS can introduce major maintance and cost burdens as described [here](http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html).
|
||||
MergerFS is not intended to be a replacement for ZFS. MergerFS is intended to provide flexible pooling of arbitrary drives (local or remote), of arbitrary sizes, and arbitrary filesystems. For `write once, read many` usecases such as bulk media storage. Where data integrity and backup is managed in other ways. In that situation ZFS can introduce a number of costs and limitations as described [here](http://louwrentius.com/the-hidden-cost-of-using-zfs-for-your-home-nas.html), [here](https://markmcb.com/2020/01/07/five-years-of-btrfs/), and [here](https://utcc.utoronto.ca/~cks/space/blog/solaris/ZFSWhyNoRealReshaping).
|
||||
|
||||
|
||||
#### Why use mergerfs over UnRAID?
|
||||
|
||||
UnRAID is a full OS and its storage layer, as I understand, is proprietary and closed source. Users who have experience with both have said they prefer the flexibility offered by mergerfs and for some the fact it is free and open source is important.
|
||||
|
||||
There are a number of UnRAID users who use mergerfs as well though I'm not entirely familiar with the use case.
|
||||
|
||||
|
||||
#### What should mergerfs NOT be used for?
|
||||
|
||||
* databases: Even if the database stored data in separate files (mergerfs wouldn't offer much otherwise) the higher latency of the indirection will kill performance. If it is a lightly used SQLITE database then it may be fine but you'll need to test.
|
||||
|
||||
* VM images: For the same reasons as databases. VM images are accessed very aggressively and mergerfs will introduce too much latency (if it works at all).
|
||||
|
||||
* As replacement for RAID: mergerfs is just for pooling branches. If you need that kind of device performance aggregation or high availability you should stick with RAID.
|
||||
|
||||
|
||||
#### Can drives be written to directly? Outside of mergerfs while pooled?
|
||||
|
||||
Yes, however its not recommended to use the same file from within the pool and from without at the same time. Especially if using caching of any kind (cache.files, cache.entry, cache.attr, cache.negative_entry, cache.symlinks, cache.readdir, etc.).
|
||||
Yes, however it's not recommended to use the same file from within the pool and from without at the same time (particularly writing). Especially if using caching of any kind (cache.files, cache.entry, cache.attr, cache.negative_entry, cache.symlinks, cache.readdir, etc.) as there could be a conflict between cached data and not.
|
||||
|
||||
|
||||
#### Why do I get an "out of space" / "no space left on device" / ENOSPC error even though there appears to be lots of space available?
|
||||
@@ -953,14 +1132,25 @@ Due to path preservation, branch tagging, read-only status, and **minfreespace**
|
||||
|
||||
It is also possible that the filesystem selected has run out of inodes. Use `df -i` to list the total and available inodes per filesystem.
|
||||
|
||||
If you don't care about path preservation then simply change the `create` policy to one which isn't. `mfs` is probably what most are looking for. The reason its not default is because it was originally set to `epmfs` and changing it now would change people's setup. Such a setting change will likely occur in mergerfs 3.
|
||||
If you don't care about path preservation then simply change the `create` policy to one which isn't. `mfs` is probably what most are looking for. The reason it's not default is because it was originally set to `epmfs` and changing it now would change people's setup. Such a setting change will likely occur in mergerfs 3.
|
||||
|
||||
|
||||
#### Why does the total available space in mergerfs not equal outside?
|
||||
|
||||
Are you using ext2/3/4? With reserve for root? mergerfs uses available space for statfs calculations. If you've reserved space for root then it won't show up.
|
||||
|
||||
You can remove the reserve by running: `tune2fs -m 0 <device>`
|
||||
|
||||
|
||||
#### Can mergerfs mounts be exported over NFS?
|
||||
|
||||
Yes. Due to current usage of libfuse by mergerfs and how NFS interacts with it it is necessary to add `noforget` to mergerfs options to keep from getting "stale file handle" errors.
|
||||
Yes, however if you do anything which may changes files out of band (including for example using the `newest` policy) it will result in "stale file handle" errors unless properly setup.
|
||||
|
||||
Some clients (Kodi) have issues in which the contents of the NFS mount will not be presented but users have found that enabling the `use_ino` option often fixes that problem.
|
||||
Be sure to use the following options:
|
||||
|
||||
* noforget
|
||||
* use_ino
|
||||
* inodecalc=path-hash
|
||||
|
||||
|
||||
#### Can mergerfs mounts be exported over Samba / SMB?
|
||||
@@ -968,42 +1158,26 @@ Some clients (Kodi) have issues in which the contents of the NFS mount will not
|
||||
Yes. While some users have reported problems it appears to always be related to how Samba is setup in relation to permissions.
|
||||
|
||||
|
||||
#### How are inodes calculated?
|
||||
#### Can mergerfs mounts be used over SSHFS?
|
||||
|
||||
mergerfs-inode = (original-inode | (device-id << 32))
|
||||
|
||||
While `ino_t` is 64 bits only a few filesystems use more than 32. Similarly, while `dev_t` is also 64 bits it was traditionally 16 bits. Bitwise or'ing them together should work most of the time. While totally unique inodes are preferred the overhead which would be needed does not seem to outweighted by the benefits.
|
||||
|
||||
While atypical, yes, inodes can be reused and not refer to the same file. The internal id used to reference a file in FUSE is different from the inode value presented. The former is the `nodeid` and is actually a tuple of (nodeid,generation). That tuple is not user facing. The inode is merely metadata passed through the kernel and found using the `stat` family of calls or `readdir`.
|
||||
|
||||
From FUSE docs regarding `use_ino`:
|
||||
|
||||
```
|
||||
Honor the st_ino field in the functions getattr() and
|
||||
fill_dir(). This value is used to fill in the st_ino field
|
||||
in the stat(2), lstat(2), fstat(2) functions and the d_ino
|
||||
field in the readdir(2) function. The filesystem does not
|
||||
have to guarantee uniqueness, however some applications
|
||||
rely on this value being unique for the whole filesystem.
|
||||
Note that this does *not* affect the inode that libfuse
|
||||
and the kernel use internally (also called the "nodeid").
|
||||
```
|
||||
Yes.
|
||||
|
||||
|
||||
#### I notice massive slowdowns of writes over NFS
|
||||
#### I notice massive slowdowns of writes when enabling cache.files.
|
||||
|
||||
Due to how NFS works and interacts with FUSE when not using `cache.files=off` or `direct_io` its possible that a getxattr for `security.capability` will be issued prior to any write. This will usually result in a massive slowdown for writes. Using `cache.files=off` or `direct_io` will keep this from happening (and generally good to enable unless you need the features it disables) but the `security_capability` option can also help by short circuiting the call and returning `ENOATTR`.
|
||||
When file caching is enabled in any form (`cache.files!=off` or `direct_io=false`) it will issue `getxattr` requests for `security.capability` prior to *every single write*. This will usually result in a performance degradation, especially when using a network filesystem (such as NFS or CIFS/SMB/Samba.) Unfortunately at this moment the kernel is not caching the response.
|
||||
|
||||
You could also set `xattr` to `noattr` or `nosys` to short circuit or stop all xattr requests.
|
||||
To work around this situation mergerfs offers a few solutions.
|
||||
|
||||
1. Set `security_capability=false`. It will short circuit any call and return `ENOATTR`. This still means though that mergerfs will receive the request before every write but at least it doesn't get passed through to the underlying filesystem.
|
||||
2. Set `xattr=noattr`. Same as above but applies to *all* calls to getxattr. Not just `security.capability`. This will not be cached by the kernel either but mergerfs' runtime config system will still function.
|
||||
3. Set `xattr=nosys`. Results in mergerfs returning `ENOSYS` which *will* be cached by the kernel. No future xattr calls will be forwarded to mergerfs. The downside is that also means the xattr based config and query functionality won't work either.
|
||||
4. Disable file caching. If you aren't using applications which use `mmap` it's probably simpler to just disable it all together. The kernel won't send the requests when caching is disabled.
|
||||
|
||||
|
||||
#### What are these .fuse_hidden files?
|
||||
|
||||
NOTE: mergerfs >= 2.26.0 will not have these temporary files. See the notes on `unlink`.
|
||||
|
||||
When not using **hard_remove** libfuse will create .fuse_hiddenXXXXXXXX files when an opened file is unlinked. This is to simplify "use after unlink" usecases. There is a possibility these files end up being picked up by software scanning directories and not ignoring hidden files. This is rarely a problem but a solution is in the works.
|
||||
|
||||
The files are cleaned up once the file is finally closed. Only if mergerfs crashes or is killed would they be left around. They are safe to remove as they are already unlinked files.
|
||||
Please upgrade. mergerfs >= 2.26.0 will not have these temporary files. See the notes on `unlink`.
|
||||
|
||||
|
||||
#### It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
|
||||
@@ -1015,30 +1189,11 @@ In Linux setreuid syscalls apply only to the thread. GLIBC hides this away by us
|
||||
For non-Linux systems mergerfs uses a read-write lock and changes credentials only when necessary. If multiple threads are to be user X then only the first one will need to change the processes credentials. So long as the other threads need to be user X they will take a readlock allowing multiple threads to share the credentials. Once a request comes in to run as user Y that thread will attempt a write lock and change to Y's credentials when it can. If the ability to give writers priority is supported then that flag will be used so threads trying to change credentials don't starve. This isn't the best solution but should work reasonably well assuming there are few users.
|
||||
|
||||
|
||||
# PERFORMANCE TWEAKING
|
||||
|
||||
NOTE: be sure to read about these features before changing them
|
||||
|
||||
* enable (or disable) `splice_move`, `splice_read`, and `splice_write`
|
||||
* increase cache timeouts `cache.attr`, `cache.entry`, `cache.negative_entry`
|
||||
* enable (or disable) page caching (`cache.files`)
|
||||
* enable `cache.open`
|
||||
* enable `cache.statfs`
|
||||
* enable `cache.symlinks`
|
||||
* enable `cache.readdir`
|
||||
* change the number of worker threads
|
||||
* disable `security_capability` and/or `xattr`
|
||||
* disable `posix_acl`
|
||||
* disable `async_read`
|
||||
* test theoretical performance using `nullrw` or mounting a ram disk
|
||||
* use `symlinkify` if your data is largely static
|
||||
* use tiered cache drives
|
||||
* use lvm and lvm cache to place a SSD in front of your HDDs (howto coming)
|
||||
|
||||
|
||||
# SUPPORT
|
||||
|
||||
Filesystems are very complex and difficult to debug. mergerfs, while being just a proxy of sorts, is also very difficult to debug given the large number of possible settings it can have itself and the massive number of environments it can run in. When reporting on a suspected issue **please, please** include as much of the below information as possible otherwise it will be difficult or impossible to diagnose. Also please make sure to read all of the above documentation as it includes nearly every known system or user issue previously encountered.
|
||||
Filesystems are complex and difficult to debug. mergerfs, while being just a proxy of sorts, is also very difficult to debug given the large number of possible settings it can have itself and the massive number of environments it can run in. When reporting on a suspected issue **please, please** include as much of the below information as possible otherwise it will be difficult or impossible to diagnose. Also please make sure to read all of the above documentation as it includes nearly every known system or user issue previously encountered.
|
||||
|
||||
**Please make sure you are using the [latest release](https://github.com/trapexit/mergerfs/releases) or have tried it in comparison. Old versions, which are often included in distros like Debian and Ubuntu, are not ever going to be updated and your bug may have been addressed already.**
|
||||
|
||||
|
||||
#### Information to include in bug reports
|
||||
@@ -1048,12 +1203,13 @@ Filesystems are very complex and difficult to debug. mergerfs, while being just
|
||||
* 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.
|
||||
* A `strace` of the app having problems:
|
||||
* `strace -f -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`
|
||||
* **Precise** directions on replicating the issue. Do not leave **anything** out.
|
||||
* Try to recreate the problem in the simplist way using standard programs.
|
||||
* Try to recreate the problem in the simplest way using standard programs.
|
||||
|
||||
|
||||
#### Contact / Issue submission
|
||||
@@ -1067,21 +1223,33 @@ Filesystems are very complex and difficult to debug. mergerfs, while being just
|
||||
|
||||
#### Support development
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
* PayPal: https://paypal.me/trapexit
|
||||
* GitHub Sponsors: https://github.com/sponsors/trapexit
|
||||
* Patreon: https://www.patreon.com/trapexit
|
||||
* SubscribeStar: https://www.subscribestar.com/trapexit
|
||||
* Bitcoin (BTC): 12CdMhEPQVmjz3SSynkAEuD5q9JmhTDCZA
|
||||
* Bitcoin Cash (BCH): 1AjPqZZhu7GVEs6JFPjHmtsvmDL4euzMzp
|
||||
* Ethereum (ETH): 0x09A166B11fCC127324C7fc5f1B572255b3046E94
|
||||
* Litecoin (LTC): LXAsq6yc6zYU3EbcqyWtHBrH1Ypx4GjUjm
|
||||
* 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
|
||||
* LBRY Credits (LBC): bFusyoZPkSuzM2Pr8mcthgvkymaosJZt5r
|
||||
* Zcash (ZEC): t1ZwTgmbQF23DJrzqbAmw8kXWvU2xUkkhTt
|
||||
* Zcoin (XZC): a8L5Vz35KdCQe7Y7urK2pcCGau7JsqZ5Gw
|
||||
* Other crypto currencies: contact me for address
|
||||
|
||||
|
||||
# LINKS
|
||||
|
||||
* https://spawn.link
|
||||
* https://github.com/trapexit/mergerfs
|
||||
* https://github.com/trapexit/mergerfs/wiki
|
||||
* https://github.com/trapexit/mergerfs-tools
|
||||
* https://github.com/trapexit/scorch
|
||||
* https://github.com/trapexit/bbf
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
case "${1}" in
|
||||
configure)
|
||||
chmod 4755 /usr/bin/mergerfs-fusermount
|
||||
;;
|
||||
esac
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@ --parallel
|
||||
|
||||
+69
-34
@@ -1,21 +1,41 @@
|
||||
VERSION = "2.9.7-mergerfs_2.29.0"
|
||||
OPTS = -O2
|
||||
VERSION = 2.9.7-mergerfs_2.30.0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
DEBUG_FLAGS := -g
|
||||
OS := $(shell uname -s)
|
||||
ifeq ($(OS),Linux)
|
||||
UTILS := utils
|
||||
INSTALLUTILS := install-utils
|
||||
else
|
||||
DEBUG_FLAGS :=
|
||||
UTILS :=
|
||||
INSTALLUTILS :=
|
||||
endif
|
||||
|
||||
DESTDIR = "/"
|
||||
BINDIR = "/bin"
|
||||
SBINDIR = "/sbin"
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
EXEC_PREFIX = $(PREFIX)
|
||||
DATAROOTDIR = $(PREFIX)/share
|
||||
DATADIR = $(DATAROOTDIR)
|
||||
BINDIR = $(EXEC_PREFIX)/bin
|
||||
SBINDIR = /sbin
|
||||
MANDIR = $(DATAROOTDIR)/man
|
||||
MAN1DIR = $(MANDIR)/man1
|
||||
|
||||
INSTALLBINDIR = $(DESTDIR)$(BINDIR)
|
||||
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
|
||||
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
|
||||
|
||||
AR ?= ar
|
||||
|
||||
SRC = \
|
||||
lib/buffer.c \
|
||||
lib/cuse_lowlevel.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/fuse.c \
|
||||
lib/fuse_kern_chan.c \
|
||||
lib/fuse_loop.c \
|
||||
lib/fuse_loop_mt.c \
|
||||
lib/fuse_lowlevel.c \
|
||||
lib/fuse_mt.c \
|
||||
@@ -26,25 +46,28 @@ SRC = \
|
||||
lib/mount.c
|
||||
OBJS = $(SRC:lib/%.c=build/%.o)
|
||||
DEPS = $(SRC:lib/%.c=build/%.d)
|
||||
|
||||
CFLAGS += \
|
||||
$(OPTS) \
|
||||
$(DEBUG_FLAGS) \
|
||||
CFLAGS ?= \
|
||||
$(OPT_FLAGS)
|
||||
CFLAGS := \
|
||||
${CFLAGS} \
|
||||
-Wall \
|
||||
-pipe \
|
||||
'-D_FILE_OFFSET_BITS=64' \
|
||||
'-D_REENTRANT' \
|
||||
'-DFUSE_USE_VERSION=29' \
|
||||
'-DFUSERMOUNT_DIR="/usr/local/bin"' \
|
||||
'-DPACKAGE_VERSION=$(VERSION)' \
|
||||
-MMD
|
||||
FUSERMOUNT_DIR = $(BINDIR)
|
||||
FUSE_FLAGS = \
|
||||
-Iinclude \
|
||||
-Ibuild \
|
||||
-MMD
|
||||
LDFLAGS += \
|
||||
-D_REENTRANT \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-DFUSE_USE_VERSION=29 \
|
||||
-DPACKAGE_VERSION=\"$(VERSION)\" \
|
||||
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-lrt \
|
||||
-pthread
|
||||
|
||||
all: build/libfuse.a
|
||||
all: build/libfuse.a $(UTILS)
|
||||
|
||||
build/config.h: build/stamp
|
||||
ecfd/build | tee build/config.h
|
||||
@@ -57,28 +80,40 @@ objects: build/config.h
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
build/libfuse.a: objects
|
||||
ar rcs build/libfuse.a $(OBJS)
|
||||
${AR} rcs build/libfuse.a $(OBJS)
|
||||
|
||||
build/mergerfs-mount: build/config.h util/fusermount.c lib/mount_util.c
|
||||
$(CC) $(CFLAGS) -Ilib -o mergerfs-mount util/fusermount.c lib/mount_util.c
|
||||
utils: mergerfs-fusermount mount.mergerfs
|
||||
|
||||
build/mount.mergerfs: build/libfuse.a util/mount.fuse.c
|
||||
$(CC) $(CFLAGS) -o mount.mergerfs util/mount.fuse.c build/libfuse.a $(LDFLAGS)
|
||||
build/mergerfs-fusermount: build/config.h util/fusermount.c lib/mount_util.c
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -Ilib -o build/mergerfs-fusermount util/fusermount.c lib/mount_util.c
|
||||
|
||||
mergerfs-fusermount: build/mergerfs-fusermount
|
||||
|
||||
build/mount.mergerfs: build/libfuse.a util/mount.mergerfs.c
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -o build/mount.mergerfs util/mount.mergerfs.c build/libfuse.a $(LDFLAGS)
|
||||
|
||||
mount.mergerfs: build/mount.mergerfs
|
||||
|
||||
build/%.o: lib/%.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
distclean: clean
|
||||
|
||||
install: build/mergerfs-mount build/mount.mergerfs
|
||||
install -D build/mergerfs-mount "$(DESTDIR)$(BINDIR)/mergerfs-mount"
|
||||
chown root:root "$(DESTDIR)$(BINDIR)/mergerfs-mount"
|
||||
chmod u+s "$(DESTDIR)$(BINDIR)/mergerfs-mount"
|
||||
install -D build/mount.mergerfs "$(DESTDIR)$(SBINDIR)/mount.mergerfs"
|
||||
strip:
|
||||
strip --strip-all build/mount.mergerfs
|
||||
strip --strip-all build/mergerfs-fusermount
|
||||
|
||||
.PHONY: objects
|
||||
install-utils: mergerfs-fusermount mount.mergerfs strip
|
||||
install -D build/mergerfs-fusermount "$(INSTALLBINDIR)/mergerfs-fusermount"
|
||||
install -D build/mount.mergerfs "$(INSTALLSBINDIR)/mount.mergerfs"
|
||||
chown root "$(INSTALLBINDIR)/mergerfs-fusermount"
|
||||
chmod u+s "$(INSTALLBINDIR)/mergerfs-fusermount"
|
||||
|
||||
install: $(INSTALLUTILS)
|
||||
|
||||
.PHONY: objects strip utils install install-utils
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: fuse
|
||||
Description: Filesystem in Userspace
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lfuse -pthread
|
||||
Libs.private: @libfuse_libs@
|
||||
Cflags: -I${includedir}/fuse -D_FILE_OFFSET_BITS=64
|
||||
@@ -1,17 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
fuseincludedir=$(includedir)/fuse
|
||||
|
||||
fuseinclude_HEADERS = \
|
||||
fuse.h \
|
||||
fuse_compat.h \
|
||||
fuse_common.h \
|
||||
fuse_common_compat.h \
|
||||
fuse_lowlevel.h \
|
||||
fuse_lowlevel_compat.h \
|
||||
fuse_opt.h \
|
||||
cuse_lowlevel.h
|
||||
|
||||
include_HEADERS = old/fuse.h ulockmgr.h
|
||||
|
||||
noinst_HEADERS = fuse_kernel.h
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
CUSE: Character device in Userspace
|
||||
Copyright (C) 2008-2009 SUSE Linux Products GmbH
|
||||
Copyright (C) 2008-2009 Tejun Heo <tj@kernel.org>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
|
||||
Read example/cusexmp.c for usages.
|
||||
*/
|
||||
|
||||
#ifndef _CUSE_LOWLEVEL_H_
|
||||
#define _CUSE_LOWLEVEL_H_
|
||||
|
||||
#ifndef FUSE_USE_VERSION
|
||||
#define FUSE_USE_VERSION 29
|
||||
#endif
|
||||
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define CUSE_UNRESTRICTED_IOCTL (1 << 0) /* use unrestricted ioctl */
|
||||
|
||||
struct fuse_session;
|
||||
|
||||
struct cuse_info {
|
||||
unsigned dev_major;
|
||||
unsigned dev_minor;
|
||||
unsigned dev_info_argc;
|
||||
const char **dev_info_argv;
|
||||
unsigned flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* Most ops behave almost identically to the matching fuse_lowlevel
|
||||
* ops except that they don't take @ino.
|
||||
*
|
||||
* init_done : called after initialization is complete
|
||||
* read/write : always direct IO, simultaneous operations allowed
|
||||
* ioctl : might be in unrestricted mode depending on ci->flags
|
||||
*/
|
||||
struct cuse_lowlevel_ops {
|
||||
void (*init) (void *userdata, struct fuse_conn_info *conn);
|
||||
void (*init_done) (void *userdata);
|
||||
void (*destroy) (void *userdata);
|
||||
void (*open) (fuse_req_t req, struct fuse_file_info *fi);
|
||||
void (*read) (fuse_req_t req, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
void (*write) (fuse_req_t req, const char *buf, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
void (*flush) (fuse_req_t req, struct fuse_file_info *fi);
|
||||
void (*release) (fuse_req_t req, struct fuse_file_info *fi);
|
||||
void (*fsync) (fuse_req_t req, int datasync, struct fuse_file_info *fi);
|
||||
void (*ioctl) (fuse_req_t req, int cmd, void *arg,
|
||||
struct fuse_file_info *fi, unsigned int flags,
|
||||
const void *in_buf, size_t in_bufsz, size_t out_bufsz);
|
||||
void (*poll) (fuse_req_t req, struct fuse_file_info *fi,
|
||||
struct fuse_pollhandle *ph);
|
||||
};
|
||||
|
||||
struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
|
||||
const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop,
|
||||
void *userdata);
|
||||
|
||||
struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
|
||||
const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop,
|
||||
int *multithreaded, void *userdata);
|
||||
|
||||
void cuse_lowlevel_teardown(struct fuse_session *se);
|
||||
|
||||
int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop, void *userdata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CUSE_LOWLEVEL_H_ */
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN_C_BEGIN extern "C" {
|
||||
#define EXTERN_C_END }
|
||||
#else
|
||||
#define EXTERN_C_BEGIN
|
||||
#define EXTERN_C_END
|
||||
#endif
|
||||
+520
-738
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_attr_s fuse_attr_t;
|
||||
struct fuse_attr_s
|
||||
{
|
||||
uint64_t ino;
|
||||
uint64_t size;
|
||||
uint64_t blocks;
|
||||
uint64_t atime;
|
||||
uint64_t mtime;
|
||||
uint64_t ctime;
|
||||
uint32_t atimensec;
|
||||
uint32_t mtimensec;
|
||||
uint32_t ctimensec;
|
||||
uint32_t mode;
|
||||
uint32_t nlink;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t rdev;
|
||||
uint32_t blksize;
|
||||
uint32_t _padding;
|
||||
};
|
||||
+179
-216
@@ -15,7 +15,10 @@
|
||||
#ifndef _FUSE_COMMON_H_
|
||||
#define _FUSE_COMMON_H_
|
||||
|
||||
#include "extern_c.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "fuse_timeouts.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -36,9 +39,7 @@
|
||||
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
|
||||
#define FUSE_MAX_MAX_PAGES 256
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Information about open files
|
||||
@@ -83,9 +84,6 @@ fuse_file_info
|
||||
|
||||
uint32_t auto_cache : 1;
|
||||
|
||||
/** Padding. Do not use*/
|
||||
uint32_t padding : 24;
|
||||
|
||||
/** File handle. May be filled in by filesystem in open().
|
||||
Available in all other file operations */
|
||||
uint64_t fh;
|
||||
@@ -109,23 +107,25 @@ fuse_file_info
|
||||
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
|
||||
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
|
||||
*/
|
||||
#define FUSE_CAP_ASYNC_READ (1 << 0)
|
||||
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_CAP_BIG_WRITES (1 << 5)
|
||||
#define FUSE_CAP_DONT_MASK (1 << 6)
|
||||
#define FUSE_CAP_SPLICE_WRITE (1 << 7)
|
||||
#define FUSE_CAP_SPLICE_MOVE (1 << 8)
|
||||
#define FUSE_CAP_SPLICE_READ (1 << 9)
|
||||
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_CAP_ASYNC_DIO (1 << 15)
|
||||
#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_CAP_POSIX_ACL (1 << 19)
|
||||
#define FUSE_CAP_CACHE_SYMLINKS (1 << 20)
|
||||
#define FUSE_CAP_MAX_PAGES (1 << 21)
|
||||
|
||||
#define FUSE_CAP_ASYNC_READ (1 << 0)
|
||||
#define FUSE_CAP_POSIX_LOCKS (1 << 1)
|
||||
#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_CAP_BIG_WRITES (1 << 5)
|
||||
#define FUSE_CAP_DONT_MASK (1 << 6)
|
||||
#define FUSE_CAP_SPLICE_WRITE (1 << 7)
|
||||
#define FUSE_CAP_SPLICE_MOVE (1 << 8)
|
||||
#define FUSE_CAP_SPLICE_READ (1 << 9)
|
||||
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_CAP_READDIR_PLUS (1 << 13)
|
||||
#define FUSE_CAP_READDIR_PLUS_AUTO (1 << 14)
|
||||
#define FUSE_CAP_ASYNC_DIO (1 << 15)
|
||||
#define FUSE_CAP_WRITEBACK_CACHE (1 << 16)
|
||||
#define FUSE_CAP_PARALLEL_DIROPS (1 << 18)
|
||||
#define FUSE_CAP_POSIX_ACL (1 << 19)
|
||||
#define FUSE_CAP_CACHE_SYMLINKS (1 << 20)
|
||||
#define FUSE_CAP_MAX_PAGES (1 << 21)
|
||||
|
||||
/**
|
||||
* Ioctl flags
|
||||
@@ -152,55 +152,55 @@ fuse_file_info
|
||||
* value must usually be smaller than the indicated value.
|
||||
*/
|
||||
struct fuse_conn_info {
|
||||
/**
|
||||
* Major version of the protocol (read-only)
|
||||
*/
|
||||
unsigned proto_major;
|
||||
/**
|
||||
* Major version of the protocol (read-only)
|
||||
*/
|
||||
unsigned proto_major;
|
||||
|
||||
/**
|
||||
* Minor version of the protocol (read-only)
|
||||
*/
|
||||
unsigned proto_minor;
|
||||
/**
|
||||
* Minor version of the protocol (read-only)
|
||||
*/
|
||||
unsigned proto_minor;
|
||||
|
||||
/**
|
||||
* Maximum size of the write buffer
|
||||
*/
|
||||
unsigned max_write;
|
||||
/**
|
||||
* Maximum size of the write buffer
|
||||
*/
|
||||
unsigned max_write;
|
||||
|
||||
/**
|
||||
* Maximum readahead
|
||||
*/
|
||||
unsigned max_readahead;
|
||||
/**
|
||||
* Maximum readahead
|
||||
*/
|
||||
unsigned max_readahead;
|
||||
|
||||
/**
|
||||
* Capability flags, that the kernel supports
|
||||
*/
|
||||
unsigned capable;
|
||||
/**
|
||||
* Capability flags, that the kernel supports
|
||||
*/
|
||||
unsigned capable;
|
||||
|
||||
/**
|
||||
* Capability flags, that the filesystem wants to enable
|
||||
*/
|
||||
unsigned want;
|
||||
/**
|
||||
* Capability flags, that the filesystem wants to enable
|
||||
*/
|
||||
unsigned want;
|
||||
|
||||
/**
|
||||
* Maximum number of backgrounded requests
|
||||
*/
|
||||
unsigned max_background;
|
||||
/**
|
||||
* Maximum number of backgrounded requests
|
||||
*/
|
||||
unsigned max_background;
|
||||
|
||||
/**
|
||||
* Kernel congestion threshold parameter
|
||||
*/
|
||||
unsigned congestion_threshold;
|
||||
/**
|
||||
* Kernel congestion threshold parameter
|
||||
*/
|
||||
unsigned congestion_threshold;
|
||||
|
||||
/**
|
||||
* Max pages
|
||||
*/
|
||||
uint16_t max_pages;
|
||||
/**
|
||||
* Max pages
|
||||
*/
|
||||
uint16_t max_pages;
|
||||
|
||||
/**
|
||||
* For future use.
|
||||
*/
|
||||
unsigned reserved[22];
|
||||
/**
|
||||
* For future use.
|
||||
*/
|
||||
unsigned reserved[22];
|
||||
};
|
||||
|
||||
struct fuse_session;
|
||||
@@ -235,7 +235,6 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
|
||||
*
|
||||
* '-f' foreground
|
||||
* '-d' '-odebug' foreground, but keep the debug option
|
||||
* '-s' single threaded
|
||||
* '-h' '--help' help
|
||||
* '-ho' help without header
|
||||
* '-ofsname=..' file system name, if not present, then set to the program
|
||||
@@ -245,12 +244,12 @@ void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
|
||||
*
|
||||
* @param args argument vector
|
||||
* @param mountpoint the returned mountpoint, should be freed after use
|
||||
* @param multithreaded set to 1 unless the '-s' option is present
|
||||
* @param foreground set to 1 if one of the relevant options is present
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint,
|
||||
int *multithreaded, int *foreground);
|
||||
int fuse_parse_cmdline(struct fuse_args *args,
|
||||
char **mountpoint,
|
||||
int *foreground);
|
||||
|
||||
/**
|
||||
* Go into the background
|
||||
@@ -282,73 +281,73 @@ void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
|
||||
* Buffer flags
|
||||
*/
|
||||
enum fuse_buf_flags {
|
||||
/**
|
||||
* Buffer contains a file descriptor
|
||||
*
|
||||
* If this flag is set, the .fd field is valid, otherwise the
|
||||
* .mem fields is valid.
|
||||
*/
|
||||
FUSE_BUF_IS_FD = (1 << 1),
|
||||
/**
|
||||
* Buffer contains a file descriptor
|
||||
*
|
||||
* If this flag is set, the .fd field is valid, otherwise the
|
||||
* .mem fields is valid.
|
||||
*/
|
||||
FUSE_BUF_IS_FD = (1 << 1),
|
||||
|
||||
/**
|
||||
* Seek on the file descriptor
|
||||
*
|
||||
* If this flag is set then the .pos field is valid and is
|
||||
* used to seek to the given offset before performing
|
||||
* operation on file descriptor.
|
||||
*/
|
||||
FUSE_BUF_FD_SEEK = (1 << 2),
|
||||
/**
|
||||
* Seek on the file descriptor
|
||||
*
|
||||
* If this flag is set then the .pos field is valid and is
|
||||
* used to seek to the given offset before performing
|
||||
* operation on file descriptor.
|
||||
*/
|
||||
FUSE_BUF_FD_SEEK = (1 << 2),
|
||||
|
||||
/**
|
||||
* Retry operation on file descriptor
|
||||
*
|
||||
* If this flag is set then retry operation on file descriptor
|
||||
* until .size bytes have been copied or an error or EOF is
|
||||
* detected.
|
||||
*/
|
||||
FUSE_BUF_FD_RETRY = (1 << 3),
|
||||
/**
|
||||
* Retry operation on file descriptor
|
||||
*
|
||||
* If this flag is set then retry operation on file descriptor
|
||||
* until .size bytes have been copied or an error or EOF is
|
||||
* detected.
|
||||
*/
|
||||
FUSE_BUF_FD_RETRY = (1 << 3),
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffer copy flags
|
||||
*/
|
||||
enum fuse_buf_copy_flags {
|
||||
/**
|
||||
* Don't use splice(2)
|
||||
*
|
||||
* Always fall back to using read and write instead of
|
||||
* splice(2) to copy data from one file descriptor to another.
|
||||
*
|
||||
* If this flag is not set, then only fall back if splice is
|
||||
* unavailable.
|
||||
*/
|
||||
FUSE_BUF_NO_SPLICE = (1 << 1),
|
||||
/**
|
||||
* Don't use splice(2)
|
||||
*
|
||||
* Always fall back to using read and write instead of
|
||||
* splice(2) to copy data from one file descriptor to another.
|
||||
*
|
||||
* If this flag is not set, then only fall back if splice is
|
||||
* unavailable.
|
||||
*/
|
||||
FUSE_BUF_NO_SPLICE = (1 << 1),
|
||||
|
||||
/**
|
||||
* Force splice
|
||||
*
|
||||
* Always use splice(2) to copy data from one file descriptor
|
||||
* to another. If splice is not available, return -EINVAL.
|
||||
*/
|
||||
FUSE_BUF_FORCE_SPLICE = (1 << 2),
|
||||
/**
|
||||
* Force splice
|
||||
*
|
||||
* Always use splice(2) to copy data from one file descriptor
|
||||
* to another. If splice is not available, return -EINVAL.
|
||||
*/
|
||||
FUSE_BUF_FORCE_SPLICE = (1 << 2),
|
||||
|
||||
/**
|
||||
* Try to move data with splice.
|
||||
*
|
||||
* If splice is used, try to move pages from the source to the
|
||||
* destination instead of copying. See documentation of
|
||||
* SPLICE_F_MOVE in splice(2) man page.
|
||||
*/
|
||||
FUSE_BUF_SPLICE_MOVE = (1 << 3),
|
||||
/**
|
||||
* Try to move data with splice.
|
||||
*
|
||||
* If splice is used, try to move pages from the source to the
|
||||
* destination instead of copying. See documentation of
|
||||
* SPLICE_F_MOVE in splice(2) man page.
|
||||
*/
|
||||
FUSE_BUF_SPLICE_MOVE = (1 << 3),
|
||||
|
||||
/**
|
||||
* Don't block on the pipe when copying data with splice
|
||||
*
|
||||
* Makes the operations on the pipe non-blocking (if the pipe
|
||||
* is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
|
||||
* man page.
|
||||
*/
|
||||
FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
|
||||
/**
|
||||
* Don't block on the pipe when copying data with splice
|
||||
*
|
||||
* Makes the operations on the pipe non-blocking (if the pipe
|
||||
* is full or empty). See SPLICE_F_NONBLOCK in the splice(2)
|
||||
* man page.
|
||||
*/
|
||||
FUSE_BUF_SPLICE_NONBLOCK= (1 << 4),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -358,36 +357,36 @@ enum fuse_buf_copy_flags {
|
||||
* be supplied as a memory pointer or as a file descriptor
|
||||
*/
|
||||
struct fuse_buf {
|
||||
/**
|
||||
* Size of data in bytes
|
||||
*/
|
||||
size_t size;
|
||||
/**
|
||||
* Size of data in bytes
|
||||
*/
|
||||
size_t size;
|
||||
|
||||
/**
|
||||
* Buffer flags
|
||||
*/
|
||||
enum fuse_buf_flags flags;
|
||||
/**
|
||||
* Buffer flags
|
||||
*/
|
||||
enum fuse_buf_flags flags;
|
||||
|
||||
/**
|
||||
* Memory pointer
|
||||
*
|
||||
* Used unless FUSE_BUF_IS_FD flag is set.
|
||||
*/
|
||||
void *mem;
|
||||
/**
|
||||
* Memory pointer
|
||||
*
|
||||
* Used unless FUSE_BUF_IS_FD flag is set.
|
||||
*/
|
||||
void *mem;
|
||||
|
||||
/**
|
||||
* File descriptor
|
||||
*
|
||||
* Used if FUSE_BUF_IS_FD flag is set.
|
||||
*/
|
||||
int fd;
|
||||
/**
|
||||
* File descriptor
|
||||
*
|
||||
* Used if FUSE_BUF_IS_FD flag is set.
|
||||
*/
|
||||
int fd;
|
||||
|
||||
/**
|
||||
* File position
|
||||
*
|
||||
* Used if FUSE_BUF_FD_SEEK flag is set.
|
||||
*/
|
||||
off_t pos;
|
||||
/**
|
||||
* File position
|
||||
*
|
||||
* Used if FUSE_BUF_FD_SEEK flag is set.
|
||||
*/
|
||||
off_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -399,41 +398,41 @@ struct fuse_buf {
|
||||
* Allocate dynamically to add more than one buffer.
|
||||
*/
|
||||
struct fuse_bufvec {
|
||||
/**
|
||||
* Number of buffers in the array
|
||||
*/
|
||||
size_t count;
|
||||
/**
|
||||
* Number of buffers in the array
|
||||
*/
|
||||
size_t count;
|
||||
|
||||
/**
|
||||
* Index of current buffer within the array
|
||||
*/
|
||||
size_t idx;
|
||||
/**
|
||||
* Index of current buffer within the array
|
||||
*/
|
||||
size_t idx;
|
||||
|
||||
/**
|
||||
* Current offset within the current buffer
|
||||
*/
|
||||
size_t off;
|
||||
/**
|
||||
* Current offset within the current buffer
|
||||
*/
|
||||
size_t off;
|
||||
|
||||
/**
|
||||
* Array of buffers
|
||||
*/
|
||||
struct fuse_buf buf[1];
|
||||
/**
|
||||
* Array of buffers
|
||||
*/
|
||||
struct fuse_buf buf[1];
|
||||
};
|
||||
|
||||
/* Initialize bufvec with a single buffer of given size */
|
||||
#define FUSE_BUFVEC_INIT(size__) \
|
||||
((struct fuse_bufvec) { \
|
||||
/* .count= */ 1, \
|
||||
/* .idx = */ 0, \
|
||||
/* .off = */ 0, \
|
||||
/* .buf = */ { /* [0] = */ { \
|
||||
/* .size = */ (size__), \
|
||||
/* .flags = */ (enum fuse_buf_flags) 0, \
|
||||
/* .mem = */ NULL, \
|
||||
/* .fd = */ -1, \
|
||||
/* .pos = */ 0, \
|
||||
} } \
|
||||
} )
|
||||
#define FUSE_BUFVEC_INIT(size__) \
|
||||
((struct fuse_bufvec) { \
|
||||
/* .count= */ 1, \
|
||||
/* .idx = */ 0, \
|
||||
/* .off = */ 0, \
|
||||
/* .buf = */ { /* [0] = */ { \
|
||||
/* .size = */ (size__), \
|
||||
/* .flags = */ (enum fuse_buf_flags) 0, \
|
||||
/* .mem = */ NULL, \
|
||||
/* .fd = */ -1, \
|
||||
/* .pos = */ 0, \
|
||||
} } \
|
||||
} )
|
||||
|
||||
/**
|
||||
* Get total size of data in a fuse buffer vector
|
||||
@@ -452,7 +451,7 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv);
|
||||
* @return actual number of bytes copied or -errno on error
|
||||
*/
|
||||
ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src,
|
||||
enum fuse_buf_copy_flags flags);
|
||||
enum fuse_buf_copy_flags flags);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Signal handling *
|
||||
@@ -479,42 +478,6 @@ int fuse_set_signal_handlers(struct fuse_session *se);
|
||||
*/
|
||||
void fuse_remove_signal_handlers(struct fuse_session *se);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Compatibility stuff *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
#if FUSE_USE_VERSION < 26
|
||||
# ifdef __FreeBSD__
|
||||
# if FUSE_USE_VERSION < 25
|
||||
# error On FreeBSD API version 25 or greater must be used
|
||||
# endif
|
||||
# endif
|
||||
# include "fuse_common_compat.h"
|
||||
# undef FUSE_MINOR_VERSION
|
||||
# undef fuse_main
|
||||
# define fuse_unmount fuse_unmount_compat22
|
||||
# if FUSE_USE_VERSION == 25
|
||||
# define FUSE_MINOR_VERSION 5
|
||||
# define fuse_mount fuse_mount_compat25
|
||||
# elif FUSE_USE_VERSION == 24 || FUSE_USE_VERSION == 22
|
||||
# define FUSE_MINOR_VERSION 4
|
||||
# define fuse_mount fuse_mount_compat22
|
||||
# elif FUSE_USE_VERSION == 21
|
||||
# define FUSE_MINOR_VERSION 1
|
||||
# define fuse_mount fuse_mount_compat22
|
||||
# elif FUSE_USE_VERSION == 11
|
||||
# warning Compatibility with API version 11 is deprecated
|
||||
# undef FUSE_MAJOR_VERSION
|
||||
# define FUSE_MAJOR_VERSION 1
|
||||
# define FUSE_MINOR_VERSION 1
|
||||
# define fuse_mount fuse_mount_compat1
|
||||
# else
|
||||
# error Compatibility with API version other than 21, 22, 24, 25 and 11 not supported
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif /* _FUSE_COMMON_H_ */
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
/* these definitions provide source compatibility to prior versions.
|
||||
Do not include this file directly! */
|
||||
|
||||
struct fuse_file_info_compat {
|
||||
int flags;
|
||||
unsigned long fh;
|
||||
int writepage;
|
||||
unsigned int direct_io : 1;
|
||||
unsigned int keep_cache : 1;
|
||||
};
|
||||
|
||||
int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args);
|
||||
|
||||
int fuse_mount_compat22(const char *mountpoint, const char *opts);
|
||||
|
||||
int fuse_mount_compat1(const char *mountpoint, const char *args[]);
|
||||
|
||||
void fuse_unmount_compat22(const char *mountpoint);
|
||||
@@ -1,201 +0,0 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
/* these definitions provide source compatibility to prior versions.
|
||||
Do not include this file directly! */
|
||||
|
||||
struct fuse_operations_compat25 {
|
||||
int (*getattr) (const char *, struct stat *);
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
|
||||
int (*mknod) (const char *, mode_t, dev_t);
|
||||
int (*mkdir) (const char *, mode_t);
|
||||
int (*unlink) (const char *);
|
||||
int (*rmdir) (const char *);
|
||||
int (*symlink) (const char *, const char *);
|
||||
int (*rename) (const char *, const char *);
|
||||
int (*link) (const char *, const char *);
|
||||
int (*chmod) (const char *, mode_t);
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*truncate) (const char *, off_t);
|
||||
int (*utime) (const char *, struct utimbuf *);
|
||||
int (*open) (const char *, struct fuse_file_info *);
|
||||
int (*read) (const char *, char *, size_t, off_t,
|
||||
struct fuse_file_info *);
|
||||
int (*write) (const char *, const char *, size_t, off_t,
|
||||
struct fuse_file_info *);
|
||||
int (*statfs) (const char *, struct statvfs *);
|
||||
int (*flush) (const char *, struct fuse_file_info *);
|
||||
int (*release) (const char *, struct fuse_file_info *);
|
||||
int (*fsync) (const char *, int, struct fuse_file_info *);
|
||||
int (*setxattr) (const char *, const char *, const char *, size_t, int);
|
||||
int (*getxattr) (const char *, const char *, char *, size_t);
|
||||
int (*listxattr) (const char *, char *, size_t);
|
||||
int (*removexattr) (const char *, const char *);
|
||||
int (*opendir) (const char *, struct fuse_file_info *);
|
||||
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
|
||||
struct fuse_file_info *);
|
||||
int (*releasedir) (const char *, struct fuse_file_info *);
|
||||
int (*fsyncdir) (const char *, int, struct fuse_file_info *);
|
||||
void *(*init) (void);
|
||||
void (*destroy) (void *);
|
||||
int (*access) (const char *, int);
|
||||
int (*create) (const char *, mode_t, struct fuse_file_info *);
|
||||
int (*ftruncate) (const char *, off_t, struct fuse_file_info *);
|
||||
int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
|
||||
};
|
||||
|
||||
struct fuse *fuse_new_compat25(int fd, struct fuse_args *args,
|
||||
const struct fuse_operations_compat25 *op,
|
||||
size_t op_size);
|
||||
|
||||
int fuse_main_real_compat25(int argc, char *argv[],
|
||||
const struct fuse_operations_compat25 *op,
|
||||
size_t op_size);
|
||||
|
||||
struct fuse *fuse_setup_compat25(int argc, char *argv[],
|
||||
const struct fuse_operations_compat25 *op,
|
||||
size_t op_size, char **mountpoint,
|
||||
int *multithreaded, int *fd);
|
||||
|
||||
void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint);
|
||||
|
||||
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
#include <sys/statfs.h>
|
||||
|
||||
struct fuse_operations_compat22 {
|
||||
int (*getattr) (const char *, struct stat *);
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
|
||||
int (*mknod) (const char *, mode_t, dev_t);
|
||||
int (*mkdir) (const char *, mode_t);
|
||||
int (*unlink) (const char *);
|
||||
int (*rmdir) (const char *);
|
||||
int (*symlink) (const char *, const char *);
|
||||
int (*rename) (const char *, const char *);
|
||||
int (*link) (const char *, const char *);
|
||||
int (*chmod) (const char *, mode_t);
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*truncate) (const char *, off_t);
|
||||
int (*utime) (const char *, struct utimbuf *);
|
||||
int (*open) (const char *, struct fuse_file_info_compat *);
|
||||
int (*read) (const char *, char *, size_t, off_t,
|
||||
struct fuse_file_info_compat *);
|
||||
int (*write) (const char *, const char *, size_t, off_t,
|
||||
struct fuse_file_info_compat *);
|
||||
int (*statfs) (const char *, struct statfs *);
|
||||
int (*flush) (const char *, struct fuse_file_info_compat *);
|
||||
int (*release) (const char *, struct fuse_file_info_compat *);
|
||||
int (*fsync) (const char *, int, struct fuse_file_info_compat *);
|
||||
int (*setxattr) (const char *, const char *, const char *, size_t, int);
|
||||
int (*getxattr) (const char *, const char *, char *, size_t);
|
||||
int (*listxattr) (const char *, char *, size_t);
|
||||
int (*removexattr) (const char *, const char *);
|
||||
int (*opendir) (const char *, struct fuse_file_info_compat *);
|
||||
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
|
||||
struct fuse_file_info_compat *);
|
||||
int (*releasedir) (const char *, struct fuse_file_info_compat *);
|
||||
int (*fsyncdir) (const char *, int, struct fuse_file_info_compat *);
|
||||
void *(*init) (void);
|
||||
void (*destroy) (void *);
|
||||
};
|
||||
|
||||
struct fuse *fuse_new_compat22(int fd, const char *opts,
|
||||
const struct fuse_operations_compat22 *op,
|
||||
size_t op_size);
|
||||
|
||||
struct fuse *fuse_setup_compat22(int argc, char *argv[],
|
||||
const struct fuse_operations_compat22 *op,
|
||||
size_t op_size, char **mountpoint,
|
||||
int *multithreaded, int *fd);
|
||||
|
||||
int fuse_main_real_compat22(int argc, char *argv[],
|
||||
const struct fuse_operations_compat22 *op,
|
||||
size_t op_size);
|
||||
|
||||
typedef int (*fuse_dirfil_t_compat) (fuse_dirh_t h, const char *name, int type);
|
||||
struct fuse_operations_compat2 {
|
||||
int (*getattr) (const char *, struct stat *);
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
|
||||
int (*mknod) (const char *, mode_t, dev_t);
|
||||
int (*mkdir) (const char *, mode_t);
|
||||
int (*unlink) (const char *);
|
||||
int (*rmdir) (const char *);
|
||||
int (*symlink) (const char *, const char *);
|
||||
int (*rename) (const char *, const char *);
|
||||
int (*link) (const char *, const char *);
|
||||
int (*chmod) (const char *, mode_t);
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*truncate) (const char *, off_t);
|
||||
int (*utime) (const char *, struct utimbuf *);
|
||||
int (*open) (const char *, int);
|
||||
int (*read) (const char *, char *, size_t, off_t);
|
||||
int (*write) (const char *, const char *, size_t, off_t);
|
||||
int (*statfs) (const char *, struct statfs *);
|
||||
int (*flush) (const char *);
|
||||
int (*release) (const char *, int);
|
||||
int (*fsync) (const char *, int);
|
||||
int (*setxattr) (const char *, const char *, const char *,
|
||||
size_t, int);
|
||||
int (*getxattr) (const char *, const char *, char *, size_t);
|
||||
int (*listxattr) (const char *, char *, size_t);
|
||||
int (*removexattr) (const char *, const char *);
|
||||
};
|
||||
|
||||
int fuse_main_compat2(int argc, char *argv[],
|
||||
const struct fuse_operations_compat2 *op);
|
||||
|
||||
struct fuse *fuse_new_compat2(int fd, const char *opts,
|
||||
const struct fuse_operations_compat2 *op);
|
||||
|
||||
struct fuse *fuse_setup_compat2(int argc, char *argv[],
|
||||
const struct fuse_operations_compat2 *op,
|
||||
char **mountpoint, int *multithreaded, int *fd);
|
||||
|
||||
struct fuse_statfs_compat1 {
|
||||
long block_size;
|
||||
long blocks;
|
||||
long blocks_free;
|
||||
long files;
|
||||
long files_free;
|
||||
long namelen;
|
||||
};
|
||||
|
||||
struct fuse_operations_compat1 {
|
||||
int (*getattr) (const char *, struct stat *);
|
||||
int (*readlink) (const char *, char *, size_t);
|
||||
int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t_compat);
|
||||
int (*mknod) (const char *, mode_t, dev_t);
|
||||
int (*mkdir) (const char *, mode_t);
|
||||
int (*unlink) (const char *);
|
||||
int (*rmdir) (const char *);
|
||||
int (*symlink) (const char *, const char *);
|
||||
int (*rename) (const char *, const char *);
|
||||
int (*link) (const char *, const char *);
|
||||
int (*chmod) (const char *, mode_t);
|
||||
int (*chown) (const char *, uid_t, gid_t);
|
||||
int (*truncate) (const char *, off_t);
|
||||
int (*utime) (const char *, struct utimbuf *);
|
||||
int (*open) (const char *, int);
|
||||
int (*read) (const char *, char *, size_t, off_t);
|
||||
int (*write) (const char *, const char *, size_t, off_t);
|
||||
int (*statfs) (struct fuse_statfs_compat1 *);
|
||||
int (*release) (const char *, int);
|
||||
int (*fsync) (const char *, int);
|
||||
};
|
||||
|
||||
#define FUSE_DEBUG_COMPAT1 (1 << 1)
|
||||
|
||||
struct fuse *fuse_new_compat1(int fd, int flags,
|
||||
const struct fuse_operations_compat1 *op);
|
||||
|
||||
void fuse_main_compat1(int argc, char *argv[],
|
||||
const struct fuse_operations_compat1 *op);
|
||||
|
||||
#endif /* __FreeBSD__ || __NetBSD__ */
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_dirent_s fuse_dirent_t;
|
||||
struct fuse_dirent_s
|
||||
{
|
||||
uint64_t ino;
|
||||
uint64_t off;
|
||||
uint32_t namelen;
|
||||
uint32_t type;
|
||||
char name[];
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_attr.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_entry.h"
|
||||
|
||||
typedef struct fuse_direntplus_s fuse_direntplus_t;
|
||||
struct fuse_direntplus_s
|
||||
{
|
||||
fuse_entry_t entry;
|
||||
fuse_attr_t attr;
|
||||
fuse_dirent_t dirent;
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#include "kvec.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "linux_dirent64.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum fuse_dirents_type_e
|
||||
{
|
||||
UNSET = 0,
|
||||
NORMAL,
|
||||
PLUS
|
||||
};
|
||||
typedef enum fuse_dirents_type_e fuse_dirents_type_t;
|
||||
|
||||
typedef struct fuse_dirents_s fuse_dirents_t;
|
||||
struct fuse_dirents_s
|
||||
{
|
||||
char *buf;
|
||||
uint64_t buf_len;
|
||||
uint64_t data_len;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
};
|
||||
|
||||
int fuse_dirents_init(fuse_dirents_t *d);
|
||||
void fuse_dirents_free(fuse_dirents_t *d);
|
||||
void fuse_dirents_reset(fuse_dirents_t *d);
|
||||
|
||||
int fuse_dirents_add(fuse_dirents_t *d,
|
||||
const struct dirent *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add_plus(fuse_dirents_t *d,
|
||||
const struct dirent *de,
|
||||
const uint64_t namelen,
|
||||
const fuse_entry_t *entry,
|
||||
const struct stat *st);
|
||||
int fuse_dirents_add_linux(fuse_dirents_t *d,
|
||||
const struct linux_dirent64 *de,
|
||||
const uint64_t namelen);
|
||||
int fuse_dirents_add_linux_plus(fuse_dirents_t *d,
|
||||
const struct linux_dirent64 *de,
|
||||
const uint64_t namelen,
|
||||
const fuse_entry_t *entry,
|
||||
const struct stat *st);
|
||||
|
||||
void *fuse_dirents_find(fuse_dirents_t *d,
|
||||
const uint64_t ino);
|
||||
|
||||
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
|
||||
|
||||
EXTERN_C_END
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_entry_s fuse_entry_t;
|
||||
struct fuse_entry_s
|
||||
{
|
||||
uint64_t nodeid;
|
||||
uint64_t generation;
|
||||
uint64_t entry_valid;
|
||||
uint64_t attr_valid;
|
||||
uint32_t entry_valid_nsec;
|
||||
uint32_t attr_valid_nsec;
|
||||
};
|
||||
@@ -38,12 +38,50 @@
|
||||
*
|
||||
* Protocol changelog:
|
||||
*
|
||||
* 7.1:
|
||||
* - add the following messages:
|
||||
* FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
|
||||
* FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
|
||||
* FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
|
||||
* FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
|
||||
* FUSE_RELEASEDIR
|
||||
* - add padding to messages to accommodate 32-bit servers on 64-bit kernels
|
||||
*
|
||||
* 7.2:
|
||||
* - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
|
||||
* - add FUSE_FSYNCDIR message
|
||||
*
|
||||
* 7.3:
|
||||
* - add FUSE_ACCESS message
|
||||
* - add FUSE_CREATE message
|
||||
* - add filehandle to fuse_setattr_in
|
||||
*
|
||||
* 7.4:
|
||||
* - add frsize to fuse_kstatfs
|
||||
* - clean up request size limit checking
|
||||
*
|
||||
* 7.5:
|
||||
* - add flags and max_write to fuse_init_out
|
||||
*
|
||||
* 7.6:
|
||||
* - add max_readahead to fuse_init_in and fuse_init_out
|
||||
*
|
||||
* 7.7:
|
||||
* - add FUSE_INTERRUPT message
|
||||
* - add POSIX file lock support
|
||||
*
|
||||
* 7.8:
|
||||
* - add lock_owner and flags fields to fuse_release_in
|
||||
* - add FUSE_BMAP message
|
||||
* - add FUSE_DESTROY message
|
||||
*
|
||||
* 7.9:
|
||||
* - new fuse_getattr_in input argument of GETATTR
|
||||
* - add lk_flags in fuse_lk_in
|
||||
* - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in
|
||||
* - add blksize field to fuse_attr
|
||||
* - add file flags field to fuse_read_in and fuse_write_in
|
||||
* - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in
|
||||
*
|
||||
* 7.10
|
||||
* - add nonseekable open flag
|
||||
@@ -54,7 +92,7 @@
|
||||
* - add POLL message and NOTIFY_POLL notification
|
||||
*
|
||||
* 7.12
|
||||
* - add umask flag to input argument of open, mknod and mkdir
|
||||
* - add umask flag to input argument of create, mknod and mkdir
|
||||
* - add notification messages for invalidation of inodes and
|
||||
* directory entries
|
||||
*
|
||||
@@ -125,6 +163,15 @@
|
||||
*
|
||||
* 7.29
|
||||
* - add FUSE_NO_OPENDIR_SUPPORT flag
|
||||
*
|
||||
* 7.30
|
||||
* - add FUSE_EXPLICIT_INVAL_DATA
|
||||
* - add FUSE_IOCTL_COMPAT_X32
|
||||
*
|
||||
* 7.31
|
||||
* - add FUSE_WRITE_KILL_PRIV flag
|
||||
* - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
|
||||
* - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@@ -160,7 +207,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 29
|
||||
#define FUSE_KERNEL_MINOR_VERSION 31
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@@ -229,11 +276,13 @@ struct fuse_file_lock {
|
||||
* FOPEN_KEEP_CACHE: don't invalidate the data cache on open
|
||||
* FOPEN_NONSEEKABLE: the file is not seekable
|
||||
* FOPEN_CACHE_DIR: allow caching this directory
|
||||
* FOPEN_STREAM: the file is stream-like (no file position at all)
|
||||
*/
|
||||
#define FOPEN_DIRECT_IO (1 << 0)
|
||||
#define FOPEN_KEEP_CACHE (1 << 1)
|
||||
#define FOPEN_NONSEEKABLE (1 << 2)
|
||||
#define FOPEN_CACHE_DIR (1 << 3)
|
||||
#define FOPEN_STREAM (1 << 4)
|
||||
|
||||
/**
|
||||
* INIT request/reply flags
|
||||
@@ -263,6 +312,8 @@ struct fuse_file_lock {
|
||||
* FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages
|
||||
* FUSE_CACHE_SYMLINKS: cache READLINK responses
|
||||
* FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
|
||||
* FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
|
||||
* FUSE_MAP_ALIGNMENT: map_alignment field is valid
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@@ -289,6 +340,8 @@ struct fuse_file_lock {
|
||||
#define FUSE_MAX_PAGES (1 << 22)
|
||||
#define FUSE_CACHE_SYMLINKS (1 << 23)
|
||||
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
|
||||
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
||||
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@@ -318,9 +371,11 @@ struct fuse_file_lock {
|
||||
*
|
||||
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
|
||||
* FUSE_WRITE_LOCKOWNER: lock_owner field is valid
|
||||
* FUSE_WRITE_KILL_PRIV: kill suid and sgid bits
|
||||
*/
|
||||
#define FUSE_WRITE_CACHE (1 << 0)
|
||||
#define FUSE_WRITE_LOCKOWNER (1 << 1)
|
||||
#define FUSE_WRITE_KILL_PRIV (1 << 2)
|
||||
|
||||
/**
|
||||
* Read flags
|
||||
@@ -335,6 +390,7 @@ struct fuse_file_lock {
|
||||
* FUSE_IOCTL_RETRY: retry with new iovecs
|
||||
* FUSE_IOCTL_32BIT: 32bit ioctl
|
||||
* FUSE_IOCTL_DIR: is a directory
|
||||
* FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t)
|
||||
*
|
||||
* FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
|
||||
*/
|
||||
@@ -343,6 +399,7 @@ struct fuse_file_lock {
|
||||
#define FUSE_IOCTL_RETRY (1 << 2)
|
||||
#define FUSE_IOCTL_32BIT (1 << 3)
|
||||
#define FUSE_IOCTL_DIR (1 << 4)
|
||||
#define FUSE_IOCTL_COMPAT_X32 (1 << 5)
|
||||
|
||||
#define FUSE_IOCTL_MAX_IOV 256
|
||||
|
||||
@@ -353,6 +410,13 @@ struct fuse_file_lock {
|
||||
*/
|
||||
#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0)
|
||||
|
||||
/**
|
||||
* Fsync flags
|
||||
*
|
||||
* FUSE_FSYNC_FDATASYNC: Sync data only, not metadata
|
||||
*/
|
||||
#define FUSE_FSYNC_FDATASYNC (1 << 0)
|
||||
|
||||
enum fuse_opcode {
|
||||
FUSE_LOOKUP = 1,
|
||||
FUSE_FORGET = 2, /* no reply */
|
||||
@@ -399,9 +463,15 @@ enum fuse_opcode {
|
||||
FUSE_RENAME2 = 45,
|
||||
FUSE_LSEEK = 46,
|
||||
FUSE_COPY_FILE_RANGE = 47,
|
||||
FUSE_SETUPMAPPING = 48,
|
||||
FUSE_REMOVEMAPPING = 49,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
|
||||
/* Reserved opcodes: helpful to detect structure endian-ness */
|
||||
CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
|
||||
FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
|
||||
};
|
||||
|
||||
enum fuse_notify_code {
|
||||
@@ -629,7 +699,7 @@ struct fuse_init_out {
|
||||
uint32_t max_write;
|
||||
uint32_t time_gran;
|
||||
uint16_t max_pages;
|
||||
uint16_t padding;
|
||||
uint16_t map_alignment;
|
||||
uint32_t unused[8];
|
||||
};
|
||||
|
||||
|
||||
+959
-1027
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
/* these definitions provide source compatibility to prior versions.
|
||||
Do not include this file directly! */
|
||||
|
||||
struct fuse_lowlevel_ops_compat25 {
|
||||
void (*init) (void *userdata);
|
||||
void (*destroy) (void *userdata);
|
||||
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
|
||||
void (*getattr) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
||||
int to_set, struct fuse_file_info *fi);
|
||||
void (*readlink) (fuse_req_t req, fuse_ino_t ino);
|
||||
void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode, dev_t rdev);
|
||||
void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode);
|
||||
void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
|
||||
const char *name);
|
||||
void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
fuse_ino_t newparent, const char *newname);
|
||||
void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
||||
const char *newname);
|
||||
void (*open) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
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 (*flush) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*release) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
void (*opendir) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi);
|
||||
void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
void (*statfs) (fuse_req_t req);
|
||||
void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
const char *value, size_t size, int flags);
|
||||
void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
size_t size);
|
||||
void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
|
||||
void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
|
||||
void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
|
||||
void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode, struct fuse_file_info *fi);
|
||||
};
|
||||
|
||||
struct fuse_session *fuse_lowlevel_new_compat25(struct fuse_args *args,
|
||||
const struct fuse_lowlevel_ops_compat25 *op,
|
||||
size_t op_size, void *userdata);
|
||||
|
||||
size_t fuse_dirent_size(size_t namelen);
|
||||
|
||||
char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
|
||||
off_t off);
|
||||
|
||||
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
|
||||
#include <sys/statfs.h>
|
||||
|
||||
struct fuse_lowlevel_ops_compat {
|
||||
void (*init) (void *userdata);
|
||||
void (*destroy) (void *userdata);
|
||||
void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
|
||||
void (*getattr) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr,
|
||||
int to_set, struct fuse_file_info_compat *fi);
|
||||
void (*readlink) (fuse_req_t req, fuse_ino_t ino);
|
||||
void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode, dev_t rdev);
|
||||
void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode);
|
||||
void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name);
|
||||
void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent,
|
||||
const char *name);
|
||||
void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
fuse_ino_t newparent, const char *newname);
|
||||
void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
||||
const char *newname);
|
||||
void (*open) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf,
|
||||
size_t size, off_t off, struct fuse_file_info_compat *fi);
|
||||
void (*flush) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*release) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*opendir) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*releasedir) (fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info_compat *fi);
|
||||
void (*statfs) (fuse_req_t req);
|
||||
void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
const char *value, size_t size, int flags);
|
||||
void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name,
|
||||
size_t size);
|
||||
void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size);
|
||||
void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name);
|
||||
void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
|
||||
void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name,
|
||||
mode_t mode, struct fuse_file_info_compat *fi);
|
||||
};
|
||||
|
||||
int fuse_reply_statfs_compat(fuse_req_t req, const struct statfs *stbuf);
|
||||
|
||||
int fuse_reply_open_compat(fuse_req_t req,
|
||||
const struct fuse_file_info_compat *fi);
|
||||
|
||||
struct fuse_session *fuse_lowlevel_new_compat(const char *opts,
|
||||
const struct fuse_lowlevel_ops_compat *op,
|
||||
size_t op_size, void *userdata);
|
||||
|
||||
#endif /* __FreeBSD__ || __NetBSD__ */
|
||||
|
||||
struct fuse_chan_ops_compat24 {
|
||||
int (*receive)(struct fuse_chan *ch, char *buf, size_t size);
|
||||
int (*send)(struct fuse_chan *ch, const struct iovec iov[],
|
||||
size_t count);
|
||||
void (*destroy)(struct fuse_chan *ch);
|
||||
};
|
||||
|
||||
struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
|
||||
int fd, size_t bufsize, void *data);
|
||||
|
||||
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size);
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd);
|
||||
+28
-28
@@ -9,14 +9,14 @@
|
||||
#ifndef _FUSE_OPT_H_
|
||||
#define _FUSE_OPT_H_
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
/** @file
|
||||
*
|
||||
* This file defines the option parsing interface of FUSE
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Option description
|
||||
@@ -73,21 +73,22 @@ extern "C" {
|
||||
* If the format is "%s", memory is allocated for the string unlike
|
||||
* with scanf().
|
||||
*/
|
||||
struct fuse_opt {
|
||||
/** Matching template and optional parameter formatting */
|
||||
const char *templ;
|
||||
struct fuse_opt
|
||||
{
|
||||
/** Matching template and optional parameter formatting */
|
||||
const char *templ;
|
||||
|
||||
/**
|
||||
* Offset of variable within 'data' parameter of fuse_opt_parse()
|
||||
* or -1
|
||||
*/
|
||||
unsigned long offset;
|
||||
/**
|
||||
* Offset of variable within 'data' parameter of fuse_opt_parse()
|
||||
* or -1
|
||||
*/
|
||||
unsigned long offset;
|
||||
|
||||
/**
|
||||
* Value to set the variable to, or to be passed as 'key' to the
|
||||
* processing function. Ignored if template has a format
|
||||
*/
|
||||
int value;
|
||||
/**
|
||||
* Value to set the variable to, or to be passed as 'key' to the
|
||||
* processing function. Ignored if template has a format
|
||||
*/
|
||||
int value;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,15 +106,16 @@ struct fuse_opt {
|
||||
/**
|
||||
* Argument list
|
||||
*/
|
||||
struct fuse_args {
|
||||
/** Argument count */
|
||||
int argc;
|
||||
struct fuse_args
|
||||
{
|
||||
/** Argument count */
|
||||
int argc;
|
||||
|
||||
/** Argument vector. NULL terminated */
|
||||
char **argv;
|
||||
/** Argument vector. NULL terminated */
|
||||
char **argv;
|
||||
|
||||
/** Is 'argv' allocated? */
|
||||
int allocated;
|
||||
/** Is 'argv' allocated? */
|
||||
int allocated;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -177,7 +179,7 @@ struct fuse_args {
|
||||
* @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
|
||||
*/
|
||||
typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs);
|
||||
struct fuse_args *outargs);
|
||||
|
||||
/**
|
||||
* Option parsing function
|
||||
@@ -200,7 +202,7 @@ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
|
||||
* @return -1 on error, 0 on success
|
||||
*/
|
||||
int fuse_opt_parse(struct fuse_args *args, void *data,
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc);
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc);
|
||||
|
||||
/**
|
||||
* Add an option to a comma separated option list
|
||||
@@ -263,8 +265,6 @@ void fuse_opt_free_args(struct fuse_args *args);
|
||||
*/
|
||||
int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
#endif /* _FUSE_OPT_H_ */
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_timeouts_s fuse_timeouts_t;
|
||||
struct fuse_timeouts_s
|
||||
{
|
||||
uint64_t entry;
|
||||
uint64_t attr;
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 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 "kvec.h"
|
||||
int main() {
|
||||
kvec_t(int) array;
|
||||
kv_init(array);
|
||||
kv_push(int, array, 10); // append
|
||||
kv_a(int, array, 20) = 5; // dynamic
|
||||
kv_A(array, 20) = 4; // static
|
||||
kv_destroy(array);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2008-09-22 (0.1.0):
|
||||
|
||||
* The initial version.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef AC_KVEC_H
|
||||
#define AC_KVEC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
|
||||
#define kvec_t(type) struct { size_t n, m; type *a; }
|
||||
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
|
||||
#define kv_destroy(v) free((v).a)
|
||||
#define kv_A(v, i) ((v).a[(i)])
|
||||
#define kv_pop(v) ((v).a[--(v).n])
|
||||
#define kv_size(v) ((v).n)
|
||||
#define kv_max(v) ((v).m)
|
||||
|
||||
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
|
||||
|
||||
#define kv_copy(type, v1, v0) do { \
|
||||
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
|
||||
(v1).n = (v0).n; \
|
||||
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
|
||||
} while (0) \
|
||||
|
||||
#define kv_push(type, v, x) do { \
|
||||
if ((v).n == (v).m) { \
|
||||
(v).m = (v).m? (v).m<<1 : 2; \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
|
||||
} \
|
||||
(v).a[(v).n++] = (x); \
|
||||
} while (0)
|
||||
|
||||
#define kv_pushp(type, v) (((v).n == (v).m)? \
|
||||
((v).m = ((v).m? (v).m<<1 : 2), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: 0), ((v).a + ((v).n++))
|
||||
|
||||
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
|
||||
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
|
||||
: 0), (v).a[(i)])
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct linux_dirent64
|
||||
{
|
||||
uint64_t ino;
|
||||
int64_t off;
|
||||
uint16_t reclen;
|
||||
uint8_t type;
|
||||
char name[];
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
This header is for compatibility with older software using FUSE.
|
||||
|
||||
Please use 'pkg-config --cflags fuse' to set include path. The
|
||||
correct usage is still '#include <fuse.h>', not '#include
|
||||
<fuse/fuse.h>'.
|
||||
*/
|
||||
|
||||
#include "fuse/fuse.h"
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
||||
#define ST_ATIM_NSEC(ST) ((ST)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(ST) ((ST)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(ST) ((ST)->st_mtim.tv_nsec)
|
||||
#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC
|
||||
#define ST_ATIM_NSEC(ST) ((ST)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(ST) ((ST)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(ST) ((ST)->st_mtimespec.tv_nsec)
|
||||
#else
|
||||
#define ST_ATIM_NSEC(ST) 0
|
||||
#define ST_CTIM_NSEC(ST) 0
|
||||
#define ST_MTIM_NSEC(ST) 0
|
||||
#endif
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
libulockmgr: Userspace Lock Manager Library
|
||||
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* Perform POSIX locking operation
|
||||
*
|
||||
* @param fd the file descriptor
|
||||
* @param cmd the locking command (F_GETFL, F_SETLK or F_SETLKW)
|
||||
* @param lock the lock parameters
|
||||
* @param owner the lock owner ID cookie
|
||||
* @param owner_len length of the lock owner ID cookie
|
||||
* @return 0 on success -errno on error
|
||||
*/
|
||||
int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
|
||||
size_t owner_len);
|
||||
@@ -1,42 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -DFUSERMOUNT_DIR=\"$(bindir)\" \
|
||||
-D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=26
|
||||
|
||||
lib_LTLIBRARIES = libfuse.la libulockmgr.la
|
||||
|
||||
if BSD
|
||||
mount_source = mount_bsd.c
|
||||
else
|
||||
mount_source = mount.c mount_util.c mount_util.h
|
||||
endif
|
||||
|
||||
libfuse_la_SOURCES = \
|
||||
fuse.c \
|
||||
fuse_i.h \
|
||||
fuse_kern_chan.c \
|
||||
fuse_loop.c \
|
||||
fuse_loop_mt.c \
|
||||
fuse_lowlevel.c \
|
||||
fuse_misc.h \
|
||||
fuse_mt.c \
|
||||
fuse_opt.c \
|
||||
fuse_session.c \
|
||||
fuse_signals.c \
|
||||
buffer.c \
|
||||
cuse_lowlevel.c \
|
||||
helper.c \
|
||||
$(mount_source)
|
||||
|
||||
libfuse_la_LDFLAGS = -pthread @libfuse_libs@ -version-number 2:9:7 \
|
||||
-Wl,--version-script,$(srcdir)/fuse_versionscript
|
||||
|
||||
if NETBSD
|
||||
libfuse_la_LIBADD = -lperfuse -lpuffs
|
||||
endif
|
||||
|
||||
libulockmgr_la_SOURCES = ulockmgr.c
|
||||
libulockmgr_la_LDFLAGS = -pthread -version-number 1:0:1
|
||||
|
||||
EXTRA_DIST = fuse_versionscript
|
||||
+202
-202
@@ -18,140 +18,140 @@
|
||||
|
||||
size_t fuse_buf_size(const struct fuse_bufvec *bufv)
|
||||
{
|
||||
size_t i;
|
||||
size_t size = 0;
|
||||
size_t i;
|
||||
size_t size = 0;
|
||||
|
||||
for (i = 0; i < bufv->count; i++) {
|
||||
if (bufv->buf[i].size == SIZE_MAX)
|
||||
size = SIZE_MAX;
|
||||
else
|
||||
size += bufv->buf[i].size;
|
||||
}
|
||||
for (i = 0; i < bufv->count; i++) {
|
||||
if (bufv->buf[i].size == SIZE_MAX)
|
||||
size = SIZE_MAX;
|
||||
else
|
||||
size += bufv->buf[i].size;
|
||||
}
|
||||
|
||||
return size;
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t min_size(size_t s1, size_t s2)
|
||||
{
|
||||
return s1 < s2 ? s1 : s2;
|
||||
return s1 < s2 ? s1 : s2;
|
||||
}
|
||||
|
||||
static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off,
|
||||
const struct fuse_buf *src, size_t src_off,
|
||||
size_t len)
|
||||
{
|
||||
ssize_t res = 0;
|
||||
size_t copied = 0;
|
||||
ssize_t res = 0;
|
||||
size_t copied = 0;
|
||||
|
||||
while (len) {
|
||||
if (dst->flags & FUSE_BUF_FD_SEEK) {
|
||||
res = pwrite(dst->fd, src->mem + src_off, len,
|
||||
dst->pos + dst_off);
|
||||
} else {
|
||||
res = write(dst->fd, src->mem + src_off, len);
|
||||
}
|
||||
if (res == -1) {
|
||||
if (!copied)
|
||||
return -errno;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
while (len) {
|
||||
if (dst->flags & FUSE_BUF_FD_SEEK) {
|
||||
res = pwrite(dst->fd, src->mem + src_off, len,
|
||||
dst->pos + dst_off);
|
||||
} else {
|
||||
res = write(dst->fd, src->mem + src_off, len);
|
||||
}
|
||||
if (res == -1) {
|
||||
if (!copied)
|
||||
return -errno;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
|
||||
copied += res;
|
||||
if (!(dst->flags & FUSE_BUF_FD_RETRY))
|
||||
break;
|
||||
copied += res;
|
||||
if (!(dst->flags & FUSE_BUF_FD_RETRY))
|
||||
break;
|
||||
|
||||
src_off += res;
|
||||
dst_off += res;
|
||||
len -= res;
|
||||
}
|
||||
src_off += res;
|
||||
dst_off += res;
|
||||
len -= res;
|
||||
}
|
||||
|
||||
return copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off,
|
||||
const struct fuse_buf *src, size_t src_off,
|
||||
size_t len)
|
||||
{
|
||||
ssize_t res = 0;
|
||||
size_t copied = 0;
|
||||
ssize_t res = 0;
|
||||
size_t copied = 0;
|
||||
|
||||
while (len) {
|
||||
if (src->flags & FUSE_BUF_FD_SEEK) {
|
||||
res = pread(src->fd, dst->mem + dst_off, len,
|
||||
src->pos + src_off);
|
||||
} else {
|
||||
res = read(src->fd, dst->mem + dst_off, len);
|
||||
}
|
||||
if (res == -1) {
|
||||
if (!copied)
|
||||
return -errno;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
while (len) {
|
||||
if (src->flags & FUSE_BUF_FD_SEEK) {
|
||||
res = pread(src->fd, dst->mem + dst_off, len,
|
||||
src->pos + src_off);
|
||||
} else {
|
||||
res = read(src->fd, dst->mem + dst_off, len);
|
||||
}
|
||||
if (res == -1) {
|
||||
if (!copied)
|
||||
return -errno;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
|
||||
copied += res;
|
||||
if (!(src->flags & FUSE_BUF_FD_RETRY))
|
||||
break;
|
||||
copied += res;
|
||||
if (!(src->flags & FUSE_BUF_FD_RETRY))
|
||||
break;
|
||||
|
||||
dst_off += res;
|
||||
src_off += res;
|
||||
len -= res;
|
||||
}
|
||||
dst_off += res;
|
||||
src_off += res;
|
||||
len -= res;
|
||||
}
|
||||
|
||||
return copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off,
|
||||
const struct fuse_buf *src, size_t src_off,
|
||||
size_t len)
|
||||
{
|
||||
char buf[4096];
|
||||
struct fuse_buf tmp = {
|
||||
.size = sizeof(buf),
|
||||
.flags = 0,
|
||||
};
|
||||
ssize_t res;
|
||||
size_t copied = 0;
|
||||
char buf[4096];
|
||||
struct fuse_buf tmp = {
|
||||
.size = sizeof(buf),
|
||||
.flags = 0,
|
||||
};
|
||||
ssize_t res;
|
||||
size_t copied = 0;
|
||||
|
||||
tmp.mem = buf;
|
||||
tmp.mem = buf;
|
||||
|
||||
while (len) {
|
||||
size_t this_len = min_size(tmp.size, len);
|
||||
size_t read_len;
|
||||
while (len) {
|
||||
size_t this_len = min_size(tmp.size, len);
|
||||
size_t read_len;
|
||||
|
||||
res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
|
||||
if (res < 0) {
|
||||
if (!copied)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
res = fuse_buf_read(&tmp, 0, src, src_off, this_len);
|
||||
if (res < 0) {
|
||||
if (!copied)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
|
||||
read_len = res;
|
||||
res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
|
||||
if (res < 0) {
|
||||
if (!copied)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
read_len = res;
|
||||
res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len);
|
||||
if (res < 0) {
|
||||
if (!copied)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
|
||||
copied += res;
|
||||
copied += res;
|
||||
|
||||
if (res < this_len)
|
||||
break;
|
||||
if (res < this_len)
|
||||
break;
|
||||
|
||||
dst_off += res;
|
||||
src_off += res;
|
||||
len -= res;
|
||||
}
|
||||
dst_off += res;
|
||||
src_off += res;
|
||||
len -= res;
|
||||
}
|
||||
|
||||
return copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SPLICE
|
||||
@@ -159,64 +159,64 @@ static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
|
||||
const struct fuse_buf *src, size_t src_off,
|
||||
size_t len, enum fuse_buf_copy_flags flags)
|
||||
{
|
||||
int splice_flags = 0;
|
||||
off_t *srcpos = NULL;
|
||||
off_t *dstpos = NULL;
|
||||
off_t srcpos_val;
|
||||
off_t dstpos_val;
|
||||
ssize_t res;
|
||||
size_t copied = 0;
|
||||
int splice_flags = 0;
|
||||
off_t *srcpos = NULL;
|
||||
off_t *dstpos = NULL;
|
||||
off_t srcpos_val;
|
||||
off_t dstpos_val;
|
||||
ssize_t res;
|
||||
size_t copied = 0;
|
||||
|
||||
if (flags & FUSE_BUF_SPLICE_MOVE)
|
||||
splice_flags |= SPLICE_F_MOVE;
|
||||
if (flags & FUSE_BUF_SPLICE_NONBLOCK)
|
||||
splice_flags |= SPLICE_F_NONBLOCK;
|
||||
if (flags & FUSE_BUF_SPLICE_MOVE)
|
||||
splice_flags |= SPLICE_F_MOVE;
|
||||
if (flags & FUSE_BUF_SPLICE_NONBLOCK)
|
||||
splice_flags |= SPLICE_F_NONBLOCK;
|
||||
|
||||
if (src->flags & FUSE_BUF_FD_SEEK) {
|
||||
srcpos_val = src->pos + src_off;
|
||||
srcpos = &srcpos_val;
|
||||
}
|
||||
if (dst->flags & FUSE_BUF_FD_SEEK) {
|
||||
dstpos_val = dst->pos + dst_off;
|
||||
dstpos = &dstpos_val;
|
||||
}
|
||||
if (src->flags & FUSE_BUF_FD_SEEK) {
|
||||
srcpos_val = src->pos + src_off;
|
||||
srcpos = &srcpos_val;
|
||||
}
|
||||
if (dst->flags & FUSE_BUF_FD_SEEK) {
|
||||
dstpos_val = dst->pos + dst_off;
|
||||
dstpos = &dstpos_val;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
res = splice(src->fd, srcpos, dst->fd, dstpos, len,
|
||||
splice_flags);
|
||||
if (res == -1) {
|
||||
if (copied)
|
||||
break;
|
||||
while (len) {
|
||||
res = splice(src->fd, srcpos, dst->fd, dstpos, len,
|
||||
splice_flags);
|
||||
if (res == -1) {
|
||||
if (copied)
|
||||
break;
|
||||
|
||||
if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
|
||||
return -errno;
|
||||
if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE))
|
||||
return -errno;
|
||||
|
||||
/* Maybe splice is not supported for this combination */
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
|
||||
len);
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
/* Maybe splice is not supported for this combination */
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off,
|
||||
len);
|
||||
}
|
||||
if (res == 0)
|
||||
break;
|
||||
|
||||
copied += res;
|
||||
if (!(src->flags & FUSE_BUF_FD_RETRY) &&
|
||||
!(dst->flags & FUSE_BUF_FD_RETRY)) {
|
||||
break;
|
||||
}
|
||||
copied += res;
|
||||
if (!(src->flags & FUSE_BUF_FD_RETRY) &&
|
||||
!(dst->flags & FUSE_BUF_FD_RETRY)) {
|
||||
break;
|
||||
}
|
||||
|
||||
len -= res;
|
||||
}
|
||||
len -= res;
|
||||
}
|
||||
|
||||
return copied;
|
||||
return copied;
|
||||
}
|
||||
#else
|
||||
static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off,
|
||||
const struct fuse_buf *src, size_t src_off,
|
||||
size_t len, enum fuse_buf_copy_flags flags)
|
||||
{
|
||||
(void) flags;
|
||||
(void) flags;
|
||||
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -225,94 +225,94 @@ static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off,
|
||||
const struct fuse_buf *src, size_t src_off,
|
||||
size_t len, enum fuse_buf_copy_flags flags)
|
||||
{
|
||||
int src_is_fd = src->flags & FUSE_BUF_IS_FD;
|
||||
int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
|
||||
int src_is_fd = src->flags & FUSE_BUF_IS_FD;
|
||||
int dst_is_fd = dst->flags & FUSE_BUF_IS_FD;
|
||||
|
||||
if (!src_is_fd && !dst_is_fd) {
|
||||
char *dstmem = dst->mem + dst_off;
|
||||
char *srcmem = src->mem + src_off;
|
||||
if (!src_is_fd && !dst_is_fd) {
|
||||
char *dstmem = dst->mem + dst_off;
|
||||
char *srcmem = src->mem + src_off;
|
||||
|
||||
if (dstmem != srcmem) {
|
||||
if (dstmem + len <= srcmem || srcmem + len <= dstmem)
|
||||
memcpy(dstmem, srcmem, len);
|
||||
else
|
||||
memmove(dstmem, srcmem, len);
|
||||
}
|
||||
if (dstmem != srcmem) {
|
||||
if (dstmem + len <= srcmem || srcmem + len <= dstmem)
|
||||
memcpy(dstmem, srcmem, len);
|
||||
else
|
||||
memmove(dstmem, srcmem, len);
|
||||
}
|
||||
|
||||
return len;
|
||||
} else if (!src_is_fd) {
|
||||
return fuse_buf_write(dst, dst_off, src, src_off, len);
|
||||
} else if (!dst_is_fd) {
|
||||
return fuse_buf_read(dst, dst_off, src, src_off, len);
|
||||
} else if (flags & FUSE_BUF_NO_SPLICE) {
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
||||
} else {
|
||||
return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
|
||||
}
|
||||
return len;
|
||||
} else if (!src_is_fd) {
|
||||
return fuse_buf_write(dst, dst_off, src, src_off, len);
|
||||
} else if (!dst_is_fd) {
|
||||
return fuse_buf_read(dst, dst_off, src, src_off, len);
|
||||
} else if (flags & FUSE_BUF_NO_SPLICE) {
|
||||
return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len);
|
||||
} else {
|
||||
return fuse_buf_splice(dst, dst_off, src, src_off, len, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv)
|
||||
{
|
||||
if (bufv->idx < bufv->count)
|
||||
return &bufv->buf[bufv->idx];
|
||||
else
|
||||
return NULL;
|
||||
if (bufv->idx < bufv->count)
|
||||
return &bufv->buf[bufv->idx];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len)
|
||||
{
|
||||
const struct fuse_buf *buf = fuse_bufvec_current(bufv);
|
||||
const struct fuse_buf *buf = fuse_bufvec_current(bufv);
|
||||
|
||||
bufv->off += len;
|
||||
assert(bufv->off <= buf->size);
|
||||
if (bufv->off == buf->size) {
|
||||
assert(bufv->idx < bufv->count);
|
||||
bufv->idx++;
|
||||
if (bufv->idx == bufv->count)
|
||||
return 0;
|
||||
bufv->off = 0;
|
||||
}
|
||||
return 1;
|
||||
bufv->off += len;
|
||||
assert(bufv->off <= buf->size);
|
||||
if (bufv->off == buf->size) {
|
||||
assert(bufv->idx < bufv->count);
|
||||
bufv->idx++;
|
||||
if (bufv->idx == bufv->count)
|
||||
return 0;
|
||||
bufv->off = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv,
|
||||
enum fuse_buf_copy_flags flags)
|
||||
{
|
||||
size_t copied = 0;
|
||||
size_t copied = 0;
|
||||
|
||||
if (dstv == srcv)
|
||||
return fuse_buf_size(dstv);
|
||||
if (dstv == srcv)
|
||||
return fuse_buf_size(dstv);
|
||||
|
||||
for (;;) {
|
||||
const struct fuse_buf *src = fuse_bufvec_current(srcv);
|
||||
const struct fuse_buf *dst = fuse_bufvec_current(dstv);
|
||||
size_t src_len;
|
||||
size_t dst_len;
|
||||
size_t len;
|
||||
ssize_t res;
|
||||
for (;;) {
|
||||
const struct fuse_buf *src = fuse_bufvec_current(srcv);
|
||||
const struct fuse_buf *dst = fuse_bufvec_current(dstv);
|
||||
size_t src_len;
|
||||
size_t dst_len;
|
||||
size_t len;
|
||||
ssize_t res;
|
||||
|
||||
if (src == NULL || dst == NULL)
|
||||
break;
|
||||
if (src == NULL || dst == NULL)
|
||||
break;
|
||||
|
||||
src_len = src->size - srcv->off;
|
||||
dst_len = dst->size - dstv->off;
|
||||
len = min_size(src_len, dst_len);
|
||||
src_len = src->size - srcv->off;
|
||||
dst_len = dst->size - dstv->off;
|
||||
len = min_size(src_len, dst_len);
|
||||
|
||||
res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
|
||||
if (res < 0) {
|
||||
if (!copied)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
copied += res;
|
||||
res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags);
|
||||
if (res < 0) {
|
||||
if (!copied)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
copied += res;
|
||||
|
||||
if (!fuse_bufvec_advance(srcv, res) ||
|
||||
!fuse_bufvec_advance(dstv, res))
|
||||
break;
|
||||
if (!fuse_bufvec_advance(srcv, res) ||
|
||||
!fuse_bufvec_advance(dstv, res))
|
||||
break;
|
||||
|
||||
if (res < len)
|
||||
break;
|
||||
}
|
||||
if (res < len)
|
||||
break;
|
||||
}
|
||||
|
||||
return copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
CUSE: Character device in Userspace
|
||||
Copyright (C) 2008 SUSE Linux Products GmbH
|
||||
Copyright (C) 2008 Tejun Heo <teheo@suse.de>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#include "cuse_lowlevel.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "fuse_misc.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct cuse_data {
|
||||
struct cuse_lowlevel_ops clop;
|
||||
unsigned max_read;
|
||||
unsigned dev_major;
|
||||
unsigned dev_minor;
|
||||
unsigned flags;
|
||||
unsigned dev_info_len;
|
||||
char dev_info[];
|
||||
};
|
||||
|
||||
static struct cuse_lowlevel_ops *req_clop(fuse_req_t req)
|
||||
{
|
||||
return &req->f->cuse_data->clop;
|
||||
}
|
||||
|
||||
static void cuse_fll_open(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->open(req, fi);
|
||||
}
|
||||
|
||||
static void cuse_fll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->read(req, size, off, fi);
|
||||
}
|
||||
|
||||
static void cuse_fll_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
|
||||
size_t size, off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->write(req, buf, size, off, fi);
|
||||
}
|
||||
|
||||
static void cuse_fll_flush(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->flush(req, fi);
|
||||
}
|
||||
|
||||
static void cuse_fll_release(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->release(req, fi);
|
||||
}
|
||||
|
||||
static void cuse_fll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->fsync(req, datasync, fi);
|
||||
}
|
||||
|
||||
static void cuse_fll_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
|
||||
struct fuse_file_info *fi, unsigned int flags,
|
||||
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->ioctl(req, cmd, arg, fi, flags, in_buf, in_bufsz,
|
||||
out_bufsz);
|
||||
}
|
||||
|
||||
static void cuse_fll_poll(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi, struct fuse_pollhandle *ph)
|
||||
{
|
||||
(void)ino;
|
||||
req_clop(req)->poll(req, fi, ph);
|
||||
}
|
||||
|
||||
static size_t cuse_pack_info(int argc, const char **argv, char *buf)
|
||||
{
|
||||
size_t size = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
size_t len;
|
||||
|
||||
len = strlen(argv[i]) + 1;
|
||||
size += len;
|
||||
if (buf) {
|
||||
memcpy(buf, argv[i], len);
|
||||
buf += len;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static struct cuse_data *cuse_prep_data(const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop)
|
||||
{
|
||||
struct cuse_data *cd;
|
||||
size_t dev_info_len;
|
||||
|
||||
dev_info_len = cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv,
|
||||
NULL);
|
||||
|
||||
if (dev_info_len > CUSE_INIT_INFO_MAX) {
|
||||
fprintf(stderr, "cuse: dev_info (%zu) too large, limit=%u\n",
|
||||
dev_info_len, CUSE_INIT_INFO_MAX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cd = calloc(1, sizeof(*cd) + dev_info_len);
|
||||
if (!cd) {
|
||||
fprintf(stderr, "cuse: failed to allocate cuse_data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(&cd->clop, clop, sizeof(cd->clop));
|
||||
cd->max_read = 131072;
|
||||
cd->dev_major = ci->dev_major;
|
||||
cd->dev_minor = ci->dev_minor;
|
||||
cd->dev_info_len = dev_info_len;
|
||||
cd->flags = ci->flags;
|
||||
cuse_pack_info(ci->dev_info_argc, ci->dev_info_argv, cd->dev_info);
|
||||
|
||||
return cd;
|
||||
}
|
||||
|
||||
struct fuse_session *cuse_lowlevel_new(struct fuse_args *args,
|
||||
const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop,
|
||||
void *userdata)
|
||||
{
|
||||
struct fuse_lowlevel_ops lop;
|
||||
struct cuse_data *cd;
|
||||
struct fuse_session *se;
|
||||
struct fuse_ll *ll;
|
||||
|
||||
cd = cuse_prep_data(ci, clop);
|
||||
if (!cd)
|
||||
return NULL;
|
||||
|
||||
memset(&lop, 0, sizeof(lop));
|
||||
lop.init = clop->init;
|
||||
lop.destroy = clop->destroy;
|
||||
lop.open = clop->open ? cuse_fll_open : NULL;
|
||||
lop.read = clop->read ? cuse_fll_read : NULL;
|
||||
lop.write = clop->write ? cuse_fll_write : NULL;
|
||||
lop.flush = clop->flush ? cuse_fll_flush : NULL;
|
||||
lop.release = clop->release ? cuse_fll_release : NULL;
|
||||
lop.fsync = clop->fsync ? cuse_fll_fsync : NULL;
|
||||
lop.ioctl = clop->ioctl ? cuse_fll_ioctl : NULL;
|
||||
lop.poll = clop->poll ? cuse_fll_poll : NULL;
|
||||
|
||||
se = fuse_lowlevel_new_common(args, &lop, sizeof(lop), userdata);
|
||||
if (!se) {
|
||||
free(cd);
|
||||
return NULL;
|
||||
}
|
||||
ll = se->data;
|
||||
ll->cuse_data = cd;
|
||||
|
||||
return se;
|
||||
}
|
||||
|
||||
static int cuse_reply_init(fuse_req_t req, struct cuse_init_out *arg,
|
||||
char *dev_info, unsigned dev_info_len)
|
||||
{
|
||||
struct iovec iov[3];
|
||||
|
||||
iov[1].iov_base = arg;
|
||||
iov[1].iov_len = sizeof(struct cuse_init_out);
|
||||
iov[2].iov_base = dev_info;
|
||||
iov[2].iov_len = dev_info_len;
|
||||
|
||||
return fuse_send_reply_iov_nofree(req, 0, iov, 3);
|
||||
}
|
||||
|
||||
void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
{
|
||||
struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
|
||||
struct cuse_init_out outarg;
|
||||
struct fuse_ll *f = req->f;
|
||||
struct cuse_data *cd = f->cuse_data;
|
||||
size_t bufsize = fuse_chan_bufsize(req->ch);
|
||||
struct cuse_lowlevel_ops *clop = req_clop(req);
|
||||
|
||||
(void) nodeid;
|
||||
if (f->debug) {
|
||||
fprintf(stderr, "CUSE_INIT: %u.%u\n", arg->major, arg->minor);
|
||||
fprintf(stderr, "flags=0x%08x\n", arg->flags);
|
||||
}
|
||||
f->conn.proto_major = arg->major;
|
||||
f->conn.proto_minor = arg->minor;
|
||||
f->conn.capable = 0;
|
||||
f->conn.want = 0;
|
||||
|
||||
if (arg->major < 7) {
|
||||
fprintf(stderr, "cuse: unsupported protocol version: %u.%u\n",
|
||||
arg->major, arg->minor);
|
||||
fuse_reply_err(req, EPROTO);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufsize < FUSE_MIN_READ_BUFFER) {
|
||||
fprintf(stderr, "cuse: warning: buffer size too small: %zu\n",
|
||||
bufsize);
|
||||
bufsize = FUSE_MIN_READ_BUFFER;
|
||||
}
|
||||
|
||||
bufsize -= 4096;
|
||||
if (bufsize < f->conn.max_write)
|
||||
f->conn.max_write = bufsize;
|
||||
|
||||
f->got_init = 1;
|
||||
if (f->op.init)
|
||||
f->op.init(f->userdata, &f->conn);
|
||||
|
||||
memset(&outarg, 0, sizeof(outarg));
|
||||
outarg.major = FUSE_KERNEL_VERSION;
|
||||
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
|
||||
outarg.flags = cd->flags;
|
||||
outarg.max_read = cd->max_read;
|
||||
outarg.max_write = f->conn.max_write;
|
||||
outarg.dev_major = cd->dev_major;
|
||||
outarg.dev_minor = cd->dev_minor;
|
||||
|
||||
if (f->debug) {
|
||||
fprintf(stderr, " CUSE_INIT: %u.%u\n",
|
||||
outarg.major, outarg.minor);
|
||||
fprintf(stderr, " flags=0x%08x\n", outarg.flags);
|
||||
fprintf(stderr, " max_read=0x%08x\n", outarg.max_read);
|
||||
fprintf(stderr, " max_write=0x%08x\n", outarg.max_write);
|
||||
fprintf(stderr, " dev_major=%u\n", outarg.dev_major);
|
||||
fprintf(stderr, " dev_minor=%u\n", outarg.dev_minor);
|
||||
fprintf(stderr, " dev_info: %.*s\n", cd->dev_info_len,
|
||||
cd->dev_info);
|
||||
}
|
||||
|
||||
cuse_reply_init(req, &outarg, cd->dev_info, cd->dev_info_len);
|
||||
|
||||
if (clop->init_done)
|
||||
clop->init_done(f->userdata);
|
||||
|
||||
fuse_free_req(req);
|
||||
}
|
||||
|
||||
struct fuse_session *cuse_lowlevel_setup(int argc, char *argv[],
|
||||
const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop,
|
||||
int *multithreaded, void *userdata)
|
||||
{
|
||||
const char *devname = "/dev/cuse";
|
||||
static const struct fuse_opt kill_subtype_opts[] = {
|
||||
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_DISCARD),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
struct fuse_session *se;
|
||||
struct fuse_chan *ch;
|
||||
int fd;
|
||||
int foreground;
|
||||
int res;
|
||||
|
||||
res = fuse_parse_cmdline(&args, NULL, multithreaded, &foreground);
|
||||
if (res == -1)
|
||||
goto err_args;
|
||||
|
||||
res = fuse_opt_parse(&args, NULL, kill_subtype_opts, NULL);
|
||||
if (res == -1)
|
||||
goto err_args;
|
||||
|
||||
/*
|
||||
* Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
|
||||
* would ensue.
|
||||
*/
|
||||
do {
|
||||
fd = open("/dev/null", O_RDWR);
|
||||
if (fd > 2)
|
||||
close(fd);
|
||||
} while (fd >= 0 && fd <= 2);
|
||||
|
||||
se = cuse_lowlevel_new(&args, ci, clop, userdata);
|
||||
fuse_opt_free_args(&args);
|
||||
if (se == NULL)
|
||||
goto err_args;
|
||||
|
||||
fd = open(devname, O_RDWR);
|
||||
if (fd == -1) {
|
||||
if (errno == ENODEV || errno == ENOENT)
|
||||
fprintf(stderr, "cuse: device not found, try 'modprobe cuse' first\n");
|
||||
else
|
||||
fprintf(stderr, "cuse: failed to open %s: %s\n",
|
||||
devname, strerror(errno));
|
||||
goto err_se;
|
||||
}
|
||||
|
||||
ch = fuse_kern_chan_new(fd);
|
||||
if (!ch) {
|
||||
close(fd);
|
||||
goto err_se;
|
||||
}
|
||||
|
||||
fuse_session_add_chan(se, ch);
|
||||
|
||||
res = fuse_set_signal_handlers(se);
|
||||
if (res == -1)
|
||||
goto err_se;
|
||||
|
||||
res = fuse_daemonize(foreground);
|
||||
if (res == -1)
|
||||
goto err_sig;
|
||||
|
||||
return se;
|
||||
|
||||
err_sig:
|
||||
fuse_remove_signal_handlers(se);
|
||||
err_se:
|
||||
fuse_session_destroy(se);
|
||||
err_args:
|
||||
fuse_opt_free_args(&args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cuse_lowlevel_teardown(struct fuse_session *se)
|
||||
{
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_session_destroy(se);
|
||||
}
|
||||
|
||||
int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
|
||||
const struct cuse_lowlevel_ops *clop, void *userdata)
|
||||
{
|
||||
struct fuse_session *se;
|
||||
int multithreaded;
|
||||
int res;
|
||||
|
||||
se = cuse_lowlevel_setup(argc, argv, ci, clop, &multithreaded,
|
||||
userdata);
|
||||
if (se == NULL)
|
||||
return 1;
|
||||
|
||||
if (multithreaded)
|
||||
res = fuse_session_loop_mt(se, 0);
|
||||
else
|
||||
res = fuse_session_loop(se);
|
||||
|
||||
cuse_lowlevel_teardown(se);
|
||||
if (res == -1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+2701
-3158
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,410 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "fuse_attr.h"
|
||||
#include "fuse_dirent.h"
|
||||
#include "fuse_direntplus.h"
|
||||
#include "fuse_dirents.h"
|
||||
#include "fuse_entry.h"
|
||||
#include "linux_dirent64.h"
|
||||
#include "stat_utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 32KB - same as glibc getdents buffer size */
|
||||
#define DEFAULT_SIZE (1024 * 32)
|
||||
|
||||
static
|
||||
uint64_t
|
||||
align_uint64_t(uint64_t v_)
|
||||
{
|
||||
return ((v_ + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1));
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
fuse_dirent_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_dirent_t,name);
|
||||
rv += namelen_;
|
||||
rv = align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
fuse_direntplus_size(const uint64_t namelen_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = offsetof(fuse_direntplus_t,dirent.name);
|
||||
rv += namelen_;
|
||||
rv = align_uint64_t(rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
fuse_dirents_buf_resize(fuse_dirents_t *d_,
|
||||
uint64_t size_)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if((d_->data_len + size_) >= d_->buf_len)
|
||||
{
|
||||
p = realloc(d_->buf,(d_->buf_len * 2));
|
||||
if(p == NULL)
|
||||
return -errno;
|
||||
|
||||
d_->buf = p;
|
||||
d_->buf_len *= 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
|
||||
uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = fuse_dirent_size(namelen_);
|
||||
|
||||
rv = fuse_dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
|
||||
d_->data_len += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
|
||||
uint64_t namelen_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t size;
|
||||
fuse_dirent_t *d;
|
||||
|
||||
size = fuse_direntplus_size(namelen_);
|
||||
|
||||
rv = fuse_dirents_buf_resize(d_,size);
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
|
||||
d_->data_len += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_dirents_fill_attr(fuse_attr_t *attr_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
attr_->ino = st_->st_ino;
|
||||
attr_->size = st_->st_size;
|
||||
attr_->blocks = st_->st_blocks;
|
||||
attr_->atime = st_->st_atime;
|
||||
attr_->mtime = st_->st_mtime;
|
||||
attr_->ctime = st_->st_ctime;
|
||||
attr_->atimensec = ST_ATIM_NSEC(st_);
|
||||
attr_->mtimensec = ST_MTIM_NSEC(st_);
|
||||
attr_->ctimensec = ST_CTIM_NSEC(st_);
|
||||
attr_->mode = st_->st_mode;
|
||||
attr_->nlink = st_->st_nlink;
|
||||
attr_->uid = st_->st_uid;
|
||||
attr_->gid = st_->st_gid;
|
||||
attr_->rdev = st_->st_rdev;
|
||||
attr_->blksize = st_->st_blksize;
|
||||
}
|
||||
|
||||
fuse_dirent_t*
|
||||
fuse_dirent_next(fuse_dirent_t *cur_)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char*)cur_;
|
||||
buf += fuse_dirent_size(cur_->namelen);
|
||||
|
||||
return (fuse_dirent_t*)buf;
|
||||
}
|
||||
|
||||
fuse_direntplus_t*
|
||||
fuse_direntplus_next(fuse_direntplus_t *cur_)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char*)cur_;
|
||||
buf += fuse_direntplus_size(cur_->dirent.namelen);
|
||||
|
||||
return (fuse_direntplus_t*)buf;
|
||||
}
|
||||
|
||||
fuse_dirent_t*
|
||||
fuse_dirent_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
fuse_dirent_t *cur;
|
||||
fuse_dirent_t *end;
|
||||
|
||||
if(d_->type != NORMAL)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_dirent_t*)&d_->buf[0];
|
||||
end = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->ino == ino_)
|
||||
return cur;
|
||||
|
||||
cur = fuse_dirent_next(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fuse_direntplus_t*
|
||||
fuse_direntplus_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
fuse_direntplus_t *cur;
|
||||
fuse_direntplus_t *end;
|
||||
|
||||
if(d_->type != PLUS)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_direntplus_t*)&d_->buf[0];
|
||||
end = (fuse_direntplus_t*)&d_->buf[d_->data_len];
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->dirent.ino == ino_)
|
||||
return cur;
|
||||
|
||||
cur = fuse_direntplus_next(cur);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void*
|
||||
fuse_dirents_find(fuse_dirents_t *d_,
|
||||
const uint64_t ino_)
|
||||
{
|
||||
switch(d_->type)
|
||||
{
|
||||
default:
|
||||
case UNSET:
|
||||
return NULL;
|
||||
case NORMAL:
|
||||
return fuse_dirent_find(d_,ino_);
|
||||
case PLUS:
|
||||
return fuse_direntplus_find(d_,ino_);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_convert_plus2normal(fuse_dirents_t *d_)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add(fuse_dirents_t *d_,
|
||||
const struct dirent *dirent_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = NORMAL;
|
||||
break;
|
||||
case NORMAL:
|
||||
break;
|
||||
case PLUS:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
d = fuse_dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
d->ino = dirent_->d_ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->d_type;
|
||||
memcpy(d->name,dirent_->d_name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_plus(fuse_dirents_t *d_,
|
||||
const struct dirent *dirent_,
|
||||
const uint64_t namelen_,
|
||||
const fuse_entry_t *entry_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
fuse_direntplus_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = PLUS;
|
||||
break;
|
||||
case NORMAL:
|
||||
return -EINVAL;
|
||||
case PLUS:
|
||||
break;
|
||||
}
|
||||
|
||||
d = fuse_dirents_direntplus_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
d->dirent.ino = dirent_->d_ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->d_type;
|
||||
memcpy(d->dirent.name,dirent_->d_name,namelen_);
|
||||
|
||||
d->entry = *entry_;
|
||||
|
||||
fuse_dirents_fill_attr(&d->attr,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||
const struct linux_dirent64 *dirent_,
|
||||
const uint64_t namelen_)
|
||||
{
|
||||
fuse_dirent_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = NORMAL;
|
||||
break;
|
||||
case NORMAL:
|
||||
break;
|
||||
case PLUS:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
d = fuse_dirents_dirent_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
d->ino = dirent_->ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->type;
|
||||
memcpy(d->name,dirent_->name,namelen_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
const struct linux_dirent64 *dirent_,
|
||||
const uint64_t namelen_,
|
||||
const fuse_entry_t *entry_,
|
||||
const struct stat *st_)
|
||||
{
|
||||
fuse_direntplus_t *d;
|
||||
|
||||
switch(d_->type)
|
||||
{
|
||||
case UNSET:
|
||||
d_->type = PLUS;
|
||||
break;
|
||||
case NORMAL:
|
||||
return -EINVAL;
|
||||
case PLUS:
|
||||
break;
|
||||
}
|
||||
|
||||
d = fuse_dirents_direntplus_alloc(d_,namelen_);
|
||||
if(d == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
d->dirent.ino = dirent_->ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->type;
|
||||
memcpy(d->dirent.name,dirent_->name,namelen_);
|
||||
|
||||
d->entry = *entry_;
|
||||
|
||||
fuse_dirents_fill_attr(&d->attr,st_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_reset(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
kv_size(d_->offs) = 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_init(fuse_dirents_t *d_)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
buf = calloc(DEFAULT_SIZE,1);
|
||||
if(buf == 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_dirents_free(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->buf_len = 0;
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_destroy(d_->offs);
|
||||
|
||||
free(d_->buf);
|
||||
}
|
||||
+75
-77
@@ -6,106 +6,108 @@
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
struct fuse_chan;
|
||||
struct fuse_ll;
|
||||
|
||||
struct fuse_session {
|
||||
struct fuse_session_ops op;
|
||||
struct fuse_session
|
||||
{
|
||||
struct fuse_session_ops op;
|
||||
|
||||
int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
|
||||
struct fuse_chan **chp);
|
||||
int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
|
||||
struct fuse_chan **chp);
|
||||
|
||||
void (*process_buf)(void *data, const struct fuse_buf *buf,
|
||||
struct fuse_chan *ch);
|
||||
void (*process_buf)(void *data, const struct fuse_buf *buf,
|
||||
struct fuse_chan *ch);
|
||||
|
||||
void *data;
|
||||
void *data;
|
||||
|
||||
volatile int exited;
|
||||
volatile int exited;
|
||||
|
||||
struct fuse_chan *ch;
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
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_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 *);
|
||||
struct fuse_notify_req *next;
|
||||
struct fuse_notify_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 *);
|
||||
struct fuse_notify_req *next;
|
||||
struct fuse_notify_req *prev;
|
||||
};
|
||||
|
||||
struct fuse_ll {
|
||||
int debug;
|
||||
int allow_root;
|
||||
int atomic_o_trunc;
|
||||
int no_remote_posix_lock;
|
||||
int no_remote_flock;
|
||||
int big_writes;
|
||||
int splice_write;
|
||||
int splice_move;
|
||||
int splice_read;
|
||||
int no_splice_write;
|
||||
int no_splice_move;
|
||||
int no_splice_read;
|
||||
struct fuse_lowlevel_ops op;
|
||||
int got_init;
|
||||
struct cuse_data *cuse_data;
|
||||
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;
|
||||
int broken_splice_nonblock;
|
||||
uint64_t notify_ctr;
|
||||
struct fuse_notify_req notify_list;
|
||||
struct fuse_ll
|
||||
{
|
||||
int debug;
|
||||
int allow_root;
|
||||
int no_remote_posix_lock;
|
||||
int no_remote_flock;
|
||||
int big_writes;
|
||||
int splice_write;
|
||||
int splice_move;
|
||||
int splice_read;
|
||||
int no_splice_write;
|
||||
int no_splice_move;
|
||||
int no_splice_read;
|
||||
struct fuse_lowlevel_ops op;
|
||||
int got_init;
|
||||
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;
|
||||
int broken_splice_nonblock;
|
||||
uint64_t notify_ctr;
|
||||
struct fuse_notify_req notify_list;
|
||||
};
|
||||
|
||||
struct fuse_cmd {
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
struct fuse_chan *ch;
|
||||
struct fuse_cmd
|
||||
{
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
struct fuse_chan *ch;
|
||||
};
|
||||
|
||||
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, int compat);
|
||||
|
||||
int fuse_sync_compat_args(struct fuse_args *args);
|
||||
size_t op_size, void *user_data);
|
||||
|
||||
struct fuse_chan *fuse_kern_chan_new(int fd);
|
||||
|
||||
struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
|
||||
const struct fuse_lowlevel_ops *op,
|
||||
size_t op_size, void *userdata);
|
||||
const struct fuse_lowlevel_ops *op,
|
||||
size_t op_size, void *userdata);
|
||||
|
||||
void fuse_kern_unmount_compat22(const char *mountpoint);
|
||||
int fuse_chan_clearfd(struct fuse_chan *ch);
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd);
|
||||
@@ -120,11 +122,7 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *multithreaded,
|
||||
int *fd,
|
||||
void *user_data,
|
||||
int compat);
|
||||
|
||||
void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg);
|
||||
void *user_data);
|
||||
|
||||
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
|
||||
|
||||
@@ -18,69 +18,69 @@
|
||||
static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
|
||||
size_t size)
|
||||
{
|
||||
struct fuse_chan *ch = *chp;
|
||||
int err;
|
||||
ssize_t res;
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
assert(se != NULL);
|
||||
struct fuse_chan *ch = *chp;
|
||||
int err;
|
||||
ssize_t res;
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
assert(se != NULL);
|
||||
|
||||
restart:
|
||||
res = read(fuse_chan_fd(ch), buf, size);
|
||||
err = errno;
|
||||
restart:
|
||||
res = read(fuse_chan_fd(ch), buf, size);
|
||||
err = errno;
|
||||
|
||||
if (fuse_session_exited(se))
|
||||
return 0;
|
||||
if (res == -1) {
|
||||
/* ENOENT means the operation was interrupted, it's safe
|
||||
to restart */
|
||||
if (err == ENOENT)
|
||||
goto restart;
|
||||
if (fuse_session_exited(se))
|
||||
return 0;
|
||||
if (res == -1) {
|
||||
/* ENOENT means the operation was interrupted, it's safe
|
||||
to restart */
|
||||
if (err == ENOENT)
|
||||
goto restart;
|
||||
|
||||
if (err == ENODEV) {
|
||||
fuse_session_exit(se);
|
||||
return 0;
|
||||
}
|
||||
/* Errors occurring during normal operation: EINTR (read
|
||||
interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
|
||||
umounted) */
|
||||
if (err != EINTR && err != EAGAIN)
|
||||
perror("fuse: reading device");
|
||||
return -err;
|
||||
}
|
||||
if ((size_t) res < sizeof(struct fuse_in_header)) {
|
||||
fprintf(stderr, "short read on fuse device\n");
|
||||
return -EIO;
|
||||
}
|
||||
return res;
|
||||
if (err == ENODEV) {
|
||||
fuse_session_exit(se);
|
||||
return 0;
|
||||
}
|
||||
/* Errors occurring during normal operation: EINTR (read
|
||||
interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
|
||||
umounted) */
|
||||
if (err != EINTR && err != EAGAIN)
|
||||
perror("fuse: reading device");
|
||||
return -err;
|
||||
}
|
||||
if ((size_t) res < sizeof(struct fuse_in_header)) {
|
||||
fprintf(stderr, "short read on fuse device\n");
|
||||
return -EIO;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec iov[],
|
||||
size_t count)
|
||||
{
|
||||
if (iov) {
|
||||
ssize_t res = writev(fuse_chan_fd(ch), iov, count);
|
||||
int err = errno;
|
||||
if (iov) {
|
||||
ssize_t res = writev(fuse_chan_fd(ch), iov, count);
|
||||
int err = errno;
|
||||
|
||||
if (res == -1) {
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
if (res == -1) {
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
|
||||
assert(se != NULL);
|
||||
assert(se != NULL);
|
||||
|
||||
/* ENOENT means the operation was interrupted */
|
||||
if (!fuse_session_exited(se) && err != ENOENT)
|
||||
perror("fuse: writing device");
|
||||
return -err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
/* ENOENT means the operation was interrupted */
|
||||
if (!fuse_session_exited(se) && err != ENOENT)
|
||||
perror("fuse: writing device");
|
||||
return -err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_kern_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
int fd = fuse_chan_fd(ch);
|
||||
int fd = fuse_chan_fd(ch);
|
||||
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
struct fuse_chan *
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
int fuse_session_loop(struct fuse_session *se)
|
||||
{
|
||||
int res = 0;
|
||||
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
|
||||
size_t bufsize = fuse_chan_bufsize(ch);
|
||||
char *buf = (char *) malloc(bufsize);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "fuse: failed to allocate read buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!fuse_session_exited(se)) {
|
||||
struct fuse_chan *tmpch = ch;
|
||||
struct fuse_buf fbuf = {
|
||||
.mem = buf,
|
||||
.size = bufsize,
|
||||
};
|
||||
|
||||
res = fuse_session_receive_buf(se, &fbuf, &tmpch);
|
||||
|
||||
if (res == -EINTR)
|
||||
continue;
|
||||
if (res <= 0)
|
||||
break;
|
||||
|
||||
fuse_session_process_buf(se, &fbuf, tmpch);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
fuse_session_reset(se);
|
||||
return res < 0 ? -1 : 0;
|
||||
}
|
||||
+135
-135
@@ -25,147 +25,147 @@
|
||||
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
|
||||
|
||||
struct fuse_worker {
|
||||
struct fuse_worker *prev;
|
||||
struct fuse_worker *next;
|
||||
pthread_t thread_id;
|
||||
size_t bufsize;
|
||||
char *buf;
|
||||
struct fuse_mt *mt;
|
||||
struct fuse_worker *prev;
|
||||
struct fuse_worker *next;
|
||||
pthread_t thread_id;
|
||||
size_t bufsize;
|
||||
char *buf;
|
||||
struct fuse_mt *mt;
|
||||
};
|
||||
|
||||
struct fuse_mt {
|
||||
struct fuse_session *se;
|
||||
struct fuse_chan *prevch;
|
||||
struct fuse_worker main;
|
||||
sem_t finish;
|
||||
int exit;
|
||||
int error;
|
||||
struct fuse_session *se;
|
||||
struct fuse_chan *prevch;
|
||||
struct fuse_worker main;
|
||||
sem_t finish;
|
||||
int exit;
|
||||
int error;
|
||||
};
|
||||
|
||||
static void list_add_worker(struct fuse_worker *w, struct fuse_worker *next)
|
||||
{
|
||||
struct fuse_worker *prev = next->prev;
|
||||
w->next = next;
|
||||
w->prev = prev;
|
||||
prev->next = w;
|
||||
next->prev = w;
|
||||
struct fuse_worker *prev = next->prev;
|
||||
w->next = next;
|
||||
w->prev = prev;
|
||||
prev->next = w;
|
||||
next->prev = w;
|
||||
}
|
||||
|
||||
static void list_del_worker(struct fuse_worker *w)
|
||||
{
|
||||
struct fuse_worker *prev = w->prev;
|
||||
struct fuse_worker *next = w->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
struct fuse_worker *prev = w->prev;
|
||||
struct fuse_worker *next = w->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
static int fuse_loop_start_thread(struct fuse_mt *mt);
|
||||
|
||||
static void *fuse_do_work(void *data)
|
||||
{
|
||||
struct fuse_worker *w = (struct fuse_worker *) data;
|
||||
struct fuse_mt *mt = w->mt;
|
||||
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)) {
|
||||
struct fuse_chan *ch = mt->prevch;
|
||||
struct fuse_buf fbuf = {
|
||||
.mem = w->buf,
|
||||
.size = w->bufsize,
|
||||
};
|
||||
int res;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if (mt->exit)
|
||||
return NULL;
|
||||
if (mt->exit)
|
||||
return NULL;
|
||||
|
||||
fuse_session_process_buf(mt->se, &fbuf, ch);
|
||||
}
|
||||
fuse_session_process_buf(mt->se, &fbuf, ch);
|
||||
}
|
||||
|
||||
sem_post(&mt->finish);
|
||||
sem_post(&mt->finish);
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
|
||||
{
|
||||
sigset_t oldset;
|
||||
sigset_t newset;
|
||||
int res;
|
||||
pthread_attr_t attr;
|
||||
char *stack_size;
|
||||
sigset_t oldset;
|
||||
sigset_t newset;
|
||||
int res;
|
||||
pthread_attr_t attr;
|
||||
char *stack_size;
|
||||
|
||||
/* Override default stack size */
|
||||
pthread_attr_init(&attr);
|
||||
stack_size = getenv(ENVNAME_THREAD_STACK);
|
||||
if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
|
||||
fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
|
||||
/* Override default stack size */
|
||||
pthread_attr_init(&attr);
|
||||
stack_size = getenv(ENVNAME_THREAD_STACK);
|
||||
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);
|
||||
res = pthread_create(thread_id, &attr, func, arg);
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
pthread_attr_destroy(&attr);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "fuse: error creating thread: %s\n",
|
||||
strerror(res));
|
||||
return -1;
|
||||
}
|
||||
/* 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);
|
||||
res = pthread_create(thread_id, &attr, func, arg);
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
pthread_attr_destroy(&attr);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "fuse: error creating thread: %s\n",
|
||||
strerror(res));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_loop_start_thread(struct fuse_mt *mt)
|
||||
{
|
||||
int res;
|
||||
struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
|
||||
if (!w) {
|
||||
fprintf(stderr, "fuse: failed to allocate worker structure\n");
|
||||
return -1;
|
||||
}
|
||||
memset(w, 0, sizeof(struct fuse_worker));
|
||||
w->bufsize = fuse_chan_bufsize(mt->prevch);
|
||||
w->buf = malloc(w->bufsize);
|
||||
w->mt = mt;
|
||||
if (!w->buf) {
|
||||
fprintf(stderr, "fuse: failed to allocate read buffer\n");
|
||||
free(w);
|
||||
return -1;
|
||||
}
|
||||
int res;
|
||||
struct fuse_worker *w = malloc(sizeof(struct fuse_worker));
|
||||
if (!w) {
|
||||
fprintf(stderr, "fuse: failed to allocate worker structure\n");
|
||||
return -1;
|
||||
}
|
||||
memset(w, 0, sizeof(struct fuse_worker));
|
||||
w->bufsize = fuse_chan_bufsize(mt->prevch);
|
||||
w->buf = calloc(w->bufsize,1);
|
||||
w->mt = mt;
|
||||
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) {
|
||||
free(w->buf);
|
||||
free(w);
|
||||
return -1;
|
||||
}
|
||||
list_add_worker(w, &mt->main);
|
||||
res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
|
||||
if (res == -1) {
|
||||
free(w->buf);
|
||||
free(w);
|
||||
return -1;
|
||||
}
|
||||
list_add_worker(w, &mt->main);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_join_worker(struct fuse_worker *w)
|
||||
{
|
||||
pthread_join(w->thread_id, NULL);
|
||||
list_del_worker(w);
|
||||
free(w->buf);
|
||||
free(w);
|
||||
pthread_join(w->thread_id, NULL);
|
||||
list_del_worker(w);
|
||||
free(w->buf);
|
||||
free(w);
|
||||
}
|
||||
|
||||
static int number_of_threads(void)
|
||||
@@ -180,46 +180,46 @@ static int number_of_threads(void)
|
||||
int fuse_session_loop_mt(struct fuse_session *se,
|
||||
const int _threads)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
int threads;
|
||||
struct fuse_mt mt;
|
||||
struct fuse_worker *w;
|
||||
int i;
|
||||
int err;
|
||||
int threads;
|
||||
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);
|
||||
mt.error = 0;
|
||||
mt.main.thread_id = pthread_self();
|
||||
mt.main.prev = mt.main.next = &mt.main;
|
||||
sem_init(&mt.finish, 0, 0);
|
||||
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);
|
||||
|
||||
threads = ((_threads > 0) ? _threads : number_of_threads());
|
||||
if(_threads < 0)
|
||||
threads /= -_threads;
|
||||
if(threads == 0)
|
||||
threads = 1;
|
||||
threads = ((_threads > 0) ? _threads : number_of_threads());
|
||||
if(_threads < 0)
|
||||
threads /= -_threads;
|
||||
if(threads == 0)
|
||||
threads = 1;
|
||||
|
||||
err = 0;
|
||||
for(i = 0; (i < threads) && !err; i++)
|
||||
err = fuse_loop_start_thread(&mt);
|
||||
err = 0;
|
||||
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);
|
||||
return err;
|
||||
sem_destroy(&mt.finish);
|
||||
fuse_session_reset(se);
|
||||
return err;
|
||||
}
|
||||
|
||||
+1858
-2026
File diff suppressed because it is too large
Load Diff
+25
-19
@@ -6,13 +6,16 @@
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
Versioned symbols cannot be used in some cases because it
|
||||
- confuse the dynamic linker in uClibc
|
||||
- not supported on MacOSX (in MachO binary format)
|
||||
- confuse the dynamic linker in uClibc
|
||||
- not supported on MacOSX (in MachO binary format)
|
||||
*/
|
||||
#if (!defined(__UCLIBC__) && !defined(__APPLE__))
|
||||
#define FUSE_SYMVER(x) __asm__(x)
|
||||
@@ -26,32 +29,35 @@
|
||||
/* Is this hack still needed? */
|
||||
static inline void fuse_mutex_init(pthread_mutex_t *mut)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
pthread_mutex_init(mut, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
|
||||
pthread_mutex_init(mut, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRUCT_STAT_ST_ATIM
|
||||
/* Linux */
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val)
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf,val) ((stbuf)->st_atim.tv_nsec = (val))
|
||||
#define ST_CTIM_NSEC_SET(stbuf,val) ((stbuf)->st_ctim.tv_nsec = (val))
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) ((stbuf)->st_mtim.tv_nsec = (val))
|
||||
#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
|
||||
/* FreeBSD */
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val)
|
||||
#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec)
|
||||
#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec)
|
||||
#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec)
|
||||
#define ST_ATIM_NSEC_SET(stbuf,val) ((stbuf)->st_atimespec.tv_nsec = (val))
|
||||
#define ST_CTIM_NSEC_SET(stbuf,val) ((stbuf)->st_ctimespec.tv_nsec = (val))
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) ((stbuf)->st_mtimespec.tv_nsec = (val))
|
||||
#else
|
||||
#define ST_ATIM_NSEC(stbuf) 0
|
||||
#define ST_CTIM_NSEC(stbuf) 0
|
||||
#define ST_MTIM_NSEC(stbuf) 0
|
||||
#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0)
|
||||
#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0)
|
||||
#define ST_ATIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#define ST_CTIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#define ST_MTIM_NSEC_SET(stbuf,val) do { } while (0)
|
||||
#endif
|
||||
|
||||
+69
-70
@@ -16,109 +16,108 @@
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct procdata {
|
||||
struct fuse *f;
|
||||
struct fuse_chan *prevch;
|
||||
struct fuse_session *prevse;
|
||||
fuse_processor_t proc;
|
||||
void *data;
|
||||
struct procdata
|
||||
{
|
||||
struct fuse *f;
|
||||
struct fuse_chan *prevch;
|
||||
struct fuse_session *prevse;
|
||||
fuse_processor_t proc;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static void mt_session_proc(void *data, const char *buf, size_t len,
|
||||
struct fuse_chan *ch)
|
||||
{
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
|
||||
|
||||
(void) len;
|
||||
(void) ch;
|
||||
pd->proc(pd->f, cmd, pd->data);
|
||||
(void) len;
|
||||
(void) ch;
|
||||
pd->proc(pd->f, cmd, pd->data);
|
||||
}
|
||||
|
||||
static void mt_session_exit(void *data, int val)
|
||||
{
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
if (val)
|
||||
fuse_session_exit(pd->prevse);
|
||||
else
|
||||
fuse_session_reset(pd->prevse);
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
if (val)
|
||||
fuse_session_exit(pd->prevse);
|
||||
else
|
||||
fuse_session_reset(pd->prevse);
|
||||
}
|
||||
|
||||
static int mt_session_exited(void *data)
|
||||
{
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
return fuse_session_exited(pd->prevse);
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
return fuse_session_exited(pd->prevse);
|
||||
}
|
||||
|
||||
static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
|
||||
{
|
||||
struct fuse_cmd *cmd;
|
||||
struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
|
||||
struct fuse_cmd *cmd;
|
||||
struct procdata *pd = (struct procdata *) fuse_chan_data(*chp);
|
||||
|
||||
assert(size >= sizeof(cmd));
|
||||
assert(size >= sizeof(cmd));
|
||||
|
||||
cmd = fuse_read_cmd(pd->f);
|
||||
if (cmd == NULL)
|
||||
return 0;
|
||||
cmd = fuse_read_cmd(pd->f);
|
||||
if (cmd == NULL)
|
||||
return 0;
|
||||
|
||||
*(struct fuse_cmd **) buf = cmd;
|
||||
*(struct fuse_cmd **)buf = cmd;
|
||||
|
||||
return sizeof(cmd);
|
||||
return sizeof(cmd);
|
||||
}
|
||||
|
||||
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
|
||||
{
|
||||
int res;
|
||||
struct procdata pd;
|
||||
struct fuse_session *prevse = fuse_get_session(f);
|
||||
struct fuse_session *se;
|
||||
struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
|
||||
struct fuse_chan *ch;
|
||||
struct fuse_session_ops sop = {
|
||||
.exit = mt_session_exit,
|
||||
.exited = mt_session_exited,
|
||||
.process = mt_session_proc,
|
||||
};
|
||||
struct fuse_chan_ops cop = {
|
||||
.receive = mt_chan_receive,
|
||||
};
|
||||
int res;
|
||||
struct procdata pd;
|
||||
struct fuse_session *prevse = fuse_get_session(f);
|
||||
struct fuse_session *se;
|
||||
struct fuse_chan *prevch = fuse_session_next_chan(prevse, NULL);
|
||||
struct fuse_chan *ch;
|
||||
struct fuse_session_ops sop = {
|
||||
.exit = mt_session_exit,
|
||||
.exited = mt_session_exited,
|
||||
.process = mt_session_proc,
|
||||
};
|
||||
struct fuse_chan_ops cop = {
|
||||
.receive = mt_chan_receive,
|
||||
};
|
||||
|
||||
pd.f = f;
|
||||
pd.prevch = prevch;
|
||||
pd.prevse = prevse;
|
||||
pd.proc = proc;
|
||||
pd.data = data;
|
||||
pd.f = f;
|
||||
pd.prevch = prevch;
|
||||
pd.prevse = prevse;
|
||||
pd.proc = proc;
|
||||
pd.data = data;
|
||||
|
||||
se = fuse_session_new(&sop, &pd);
|
||||
if (se == NULL)
|
||||
return -1;
|
||||
se = fuse_session_new(&sop, &pd);
|
||||
if (se == NULL)
|
||||
return -1;
|
||||
|
||||
ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
|
||||
sizeof(struct fuse_cmd *), &pd);
|
||||
if (ch == NULL) {
|
||||
fuse_session_destroy(se);
|
||||
return -1;
|
||||
}
|
||||
fuse_session_add_chan(se, ch);
|
||||
res = fuse_session_loop_mt(se,
|
||||
fuse_config_num_threads(f));
|
||||
fuse_session_destroy(se);
|
||||
return res;
|
||||
ch = fuse_chan_new(&cop, fuse_chan_fd(prevch),
|
||||
sizeof(struct fuse_cmd *), &pd);
|
||||
if (ch == NULL) {
|
||||
fuse_session_destroy(se);
|
||||
return -1;
|
||||
}
|
||||
fuse_session_add_chan(se, ch);
|
||||
res = fuse_session_loop_mt(se,
|
||||
fuse_config_num_threads(f));
|
||||
fuse_session_destroy(se);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_loop_mt(struct fuse *f)
|
||||
{
|
||||
if (f == NULL)
|
||||
return -1;
|
||||
if (f == NULL)
|
||||
return -1;
|
||||
|
||||
int res = fuse_start_cleanup_thread(f);
|
||||
if (res)
|
||||
return -1;
|
||||
int res = fuse_start_cleanup_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);
|
||||
return res;
|
||||
res = fuse_session_loop_mt(fuse_get_session(f),
|
||||
fuse_config_num_threads(f));
|
||||
fuse_stop_cleanup_thread(f);
|
||||
return res;
|
||||
}
|
||||
|
||||
FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
|
||||
|
||||
+275
-280
@@ -14,413 +14,408 @@
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct fuse_opt_context {
|
||||
void *data;
|
||||
const struct fuse_opt *opt;
|
||||
fuse_opt_proc_t proc;
|
||||
int argctr;
|
||||
int argc;
|
||||
char **argv;
|
||||
struct fuse_args outargs;
|
||||
char *opts;
|
||||
int nonopt;
|
||||
struct fuse_opt_context
|
||||
{
|
||||
void *data;
|
||||
const struct fuse_opt *opt;
|
||||
fuse_opt_proc_t proc;
|
||||
int argctr;
|
||||
int argc;
|
||||
char **argv;
|
||||
struct fuse_args outargs;
|
||||
char *opts;
|
||||
int nonopt;
|
||||
};
|
||||
|
||||
void fuse_opt_free_args(struct fuse_args *args)
|
||||
void
|
||||
fuse_opt_free_args(struct fuse_args *args)
|
||||
{
|
||||
if (args) {
|
||||
if (args->argv && args->allocated) {
|
||||
int i;
|
||||
for (i = 0; i < args->argc; i++)
|
||||
free(args->argv[i]);
|
||||
free(args->argv);
|
||||
}
|
||||
args->argc = 0;
|
||||
args->argv = NULL;
|
||||
args->allocated = 0;
|
||||
}
|
||||
if (args) {
|
||||
if (args->argv && args->allocated) {
|
||||
int i;
|
||||
for (i = 0; i < args->argc; i++)
|
||||
free(args->argv[i]);
|
||||
free(args->argv);
|
||||
}
|
||||
args->argc = 0;
|
||||
args->argv = NULL;
|
||||
args->allocated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int alloc_failed(void)
|
||||
static
|
||||
int
|
||||
alloc_failed(void)
|
||||
{
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
|
||||
int
|
||||
fuse_opt_add_arg(struct fuse_args *args, const char *arg)
|
||||
{
|
||||
char **newargv;
|
||||
char *newarg;
|
||||
char **newargv;
|
||||
char *newarg;
|
||||
|
||||
assert(!args->argv || args->allocated);
|
||||
assert(!args->argv || args->allocated);
|
||||
|
||||
newarg = strdup(arg);
|
||||
if (!newarg)
|
||||
return alloc_failed();
|
||||
newarg = strdup(arg);
|
||||
if (!newarg)
|
||||
return alloc_failed();
|
||||
|
||||
newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
||||
if (!newargv) {
|
||||
free(newarg);
|
||||
return alloc_failed();
|
||||
}
|
||||
newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
||||
if (!newargv) {
|
||||
free(newarg);
|
||||
return alloc_failed();
|
||||
}
|
||||
|
||||
args->argv = newargv;
|
||||
args->allocated = 1;
|
||||
args->argv[args->argc++] = newarg;
|
||||
args->argv[args->argc] = NULL;
|
||||
return 0;
|
||||
args->argv = newargv;
|
||||
args->allocated = 1;
|
||||
args->argv[args->argc++] = newarg;
|
||||
args->argv[args->argc] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
|
||||
const char *arg)
|
||||
{
|
||||
assert(pos <= args->argc);
|
||||
if (fuse_opt_add_arg(args, arg) == -1)
|
||||
return -1;
|
||||
assert(pos <= args->argc);
|
||||
if (fuse_opt_add_arg(args, arg) == -1)
|
||||
return -1;
|
||||
|
||||
if (pos != args->argc - 1) {
|
||||
char *newarg = args->argv[args->argc - 1];
|
||||
memmove(&args->argv[pos + 1], &args->argv[pos],
|
||||
sizeof(char *) * (args->argc - pos - 1));
|
||||
args->argv[pos] = newarg;
|
||||
}
|
||||
return 0;
|
||||
if (pos != args->argc - 1) {
|
||||
char *newarg = args->argv[args->argc - 1];
|
||||
memmove(&args->argv[pos + 1], &args->argv[pos],
|
||||
sizeof(char *) * (args->argc - pos - 1));
|
||||
args->argv[pos] = newarg;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
|
||||
{
|
||||
return fuse_opt_insert_arg_common(args, pos, arg);
|
||||
}
|
||||
|
||||
int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos,
|
||||
const char *arg);
|
||||
int fuse_opt_insert_arg_compat(struct fuse_args *args, int pos, const char *arg)
|
||||
{
|
||||
return fuse_opt_insert_arg_common(args, pos, arg);
|
||||
return fuse_opt_insert_arg_common(args, pos, arg);
|
||||
}
|
||||
|
||||
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
if (ctx->argctr + 1 >= ctx->argc) {
|
||||
fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
|
||||
return -1;
|
||||
}
|
||||
ctx->argctr++;
|
||||
return 0;
|
||||
if (ctx->argctr + 1 >= ctx->argc) {
|
||||
fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
|
||||
return -1;
|
||||
}
|
||||
ctx->argctr++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_arg(struct fuse_opt_context *ctx, const char *arg)
|
||||
{
|
||||
return fuse_opt_add_arg(&ctx->outargs, arg);
|
||||
return fuse_opt_add_arg(&ctx->outargs, arg);
|
||||
}
|
||||
|
||||
static int add_opt_common(char **opts, const char *opt, int esc)
|
||||
{
|
||||
unsigned oldlen = *opts ? strlen(*opts) : 0;
|
||||
char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
|
||||
unsigned oldlen = *opts ? strlen(*opts) : 0;
|
||||
char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
|
||||
|
||||
if (!d)
|
||||
return alloc_failed();
|
||||
if (!d)
|
||||
return alloc_failed();
|
||||
|
||||
*opts = d;
|
||||
if (oldlen) {
|
||||
d += oldlen;
|
||||
*d++ = ',';
|
||||
}
|
||||
*opts = d;
|
||||
if (oldlen) {
|
||||
d += oldlen;
|
||||
*d++ = ',';
|
||||
}
|
||||
|
||||
for (; *opt; opt++) {
|
||||
if (esc && (*opt == ',' || *opt == '\\'))
|
||||
*d++ = '\\';
|
||||
*d++ = *opt;
|
||||
}
|
||||
*d = '\0';
|
||||
for (; *opt; opt++) {
|
||||
if (esc && (*opt == ',' || *opt == '\\'))
|
||||
*d++ = '\\';
|
||||
*d++ = *opt;
|
||||
}
|
||||
*d = '\0';
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_add_opt(char **opts, const char *opt)
|
||||
{
|
||||
return add_opt_common(opts, opt, 0);
|
||||
return add_opt_common(opts, opt, 0);
|
||||
}
|
||||
|
||||
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
|
||||
{
|
||||
return add_opt_common(opts, opt, 1);
|
||||
return add_opt_common(opts, opt, 1);
|
||||
}
|
||||
|
||||
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
return add_opt_common(&ctx->opts, opt, 1);
|
||||
return add_opt_common(&ctx->opts, opt, 1);
|
||||
}
|
||||
|
||||
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
|
||||
int iso)
|
||||
{
|
||||
if (key == FUSE_OPT_KEY_DISCARD)
|
||||
return 0;
|
||||
if (key == FUSE_OPT_KEY_DISCARD)
|
||||
return 0;
|
||||
|
||||
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
|
||||
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
|
||||
if (res == -1 || !res)
|
||||
return res;
|
||||
}
|
||||
if (iso)
|
||||
return add_opt(ctx, arg);
|
||||
else
|
||||
return add_arg(ctx, arg);
|
||||
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
|
||||
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
|
||||
if (res == -1 || !res)
|
||||
return res;
|
||||
}
|
||||
if (iso)
|
||||
return add_opt(ctx, arg);
|
||||
else
|
||||
return add_arg(ctx, arg);
|
||||
}
|
||||
|
||||
static int match_template(const char *t, const char *arg, unsigned *sepp)
|
||||
{
|
||||
int arglen = strlen(arg);
|
||||
const char *sep = strchr(t, '=');
|
||||
sep = sep ? sep : strchr(t, ' ');
|
||||
if (sep && (!sep[1] || sep[1] == '%')) {
|
||||
int tlen = sep - t;
|
||||
if (sep[0] == '=')
|
||||
tlen ++;
|
||||
if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
|
||||
*sepp = sep - t;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (strcmp(t, arg) == 0) {
|
||||
*sepp = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
int arglen = strlen(arg);
|
||||
const char *sep = strchr(t, '=');
|
||||
sep = sep ? sep : strchr(t, ' ');
|
||||
if (sep && (!sep[1] || sep[1] == '%')) {
|
||||
int tlen = sep - t;
|
||||
if (sep[0] == '=')
|
||||
tlen ++;
|
||||
if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
|
||||
*sepp = sep - t;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (strcmp(t, arg) == 0) {
|
||||
*sepp = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
|
||||
const char *arg, unsigned *sepp)
|
||||
{
|
||||
for (; opt && opt->templ; opt++)
|
||||
if (match_template(opt->templ, arg, sepp))
|
||||
return opt;
|
||||
return NULL;
|
||||
for (; opt && opt->templ; opt++)
|
||||
if (match_template(opt->templ, arg, sepp))
|
||||
return opt;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
|
||||
{
|
||||
unsigned dummy;
|
||||
return find_opt(opts, opt, &dummy) ? 1 : 0;
|
||||
unsigned dummy;
|
||||
return find_opt(opts, opt, &dummy) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int process_opt_param(void *var, const char *format, const char *param,
|
||||
const char *arg)
|
||||
{
|
||||
assert(format[0] == '%');
|
||||
if (format[1] == 's') {
|
||||
char *copy = strdup(param);
|
||||
if (!copy)
|
||||
return alloc_failed();
|
||||
assert(format[0] == '%');
|
||||
if (format[1] == 's') {
|
||||
char *copy = strdup(param);
|
||||
if (!copy)
|
||||
return alloc_failed();
|
||||
|
||||
*(char **) var = copy;
|
||||
} else {
|
||||
if (sscanf(param, format, var) != 1) {
|
||||
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
*(char **) var = copy;
|
||||
} else {
|
||||
if (sscanf(param, format, var) != 1) {
|
||||
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_opt(struct fuse_opt_context *ctx,
|
||||
const struct fuse_opt *opt, unsigned sep,
|
||||
const char *arg, int iso)
|
||||
{
|
||||
if (opt->offset == -1U) {
|
||||
if (call_proc(ctx, arg, opt->value, iso) == -1)
|
||||
return -1;
|
||||
} else {
|
||||
void *var = ctx->data + opt->offset;
|
||||
if (sep && opt->templ[sep + 1]) {
|
||||
const char *param = arg + sep;
|
||||
if (opt->templ[sep] == '=')
|
||||
param ++;
|
||||
if (process_opt_param(var, opt->templ + sep + 1,
|
||||
param, arg) == -1)
|
||||
return -1;
|
||||
} else
|
||||
*(int *)var = opt->value;
|
||||
}
|
||||
return 0;
|
||||
if (opt->offset == -1U) {
|
||||
if (call_proc(ctx, arg, opt->value, iso) == -1)
|
||||
return -1;
|
||||
} else {
|
||||
void *var = ctx->data + opt->offset;
|
||||
if (sep && opt->templ[sep + 1]) {
|
||||
const char *param = arg + sep;
|
||||
if (opt->templ[sep] == '=')
|
||||
param ++;
|
||||
if (process_opt_param(var, opt->templ + sep + 1,
|
||||
param, arg) == -1)
|
||||
return -1;
|
||||
} else
|
||||
*(int *)var = opt->value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_opt_sep_arg(struct fuse_opt_context *ctx,
|
||||
const struct fuse_opt *opt, unsigned sep,
|
||||
const char *arg, int iso)
|
||||
{
|
||||
int res;
|
||||
char *newarg;
|
||||
char *param;
|
||||
int res;
|
||||
char *newarg;
|
||||
char *param;
|
||||
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
param = ctx->argv[ctx->argctr];
|
||||
newarg = malloc(sep + strlen(param) + 1);
|
||||
if (!newarg)
|
||||
return alloc_failed();
|
||||
param = ctx->argv[ctx->argctr];
|
||||
newarg = malloc(sep + strlen(param) + 1);
|
||||
if (!newarg)
|
||||
return alloc_failed();
|
||||
|
||||
memcpy(newarg, arg, sep);
|
||||
strcpy(newarg + sep, param);
|
||||
res = process_opt(ctx, opt, sep, newarg, iso);
|
||||
free(newarg);
|
||||
memcpy(newarg, arg, sep);
|
||||
strcpy(newarg + sep, param);
|
||||
res = process_opt(ctx, opt, sep, newarg, iso);
|
||||
free(newarg);
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
|
||||
{
|
||||
unsigned sep;
|
||||
const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
|
||||
if (opt) {
|
||||
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
|
||||
int res;
|
||||
if (sep && opt->templ[sep] == ' ' && !arg[sep])
|
||||
res = process_opt_sep_arg(ctx, opt, sep, arg,
|
||||
iso);
|
||||
else
|
||||
res = process_opt(ctx, opt, sep, arg, iso);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
|
||||
unsigned sep;
|
||||
const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
|
||||
if (opt) {
|
||||
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
|
||||
int res;
|
||||
if (sep && opt->templ[sep] == ' ' && !arg[sep])
|
||||
res = process_opt_sep_arg(ctx, opt, sep, arg,
|
||||
iso);
|
||||
else
|
||||
res = process_opt(ctx, opt, sep, arg, iso);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
|
||||
}
|
||||
|
||||
static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
|
||||
{
|
||||
char *s = opts;
|
||||
char *d = s;
|
||||
int end = 0;
|
||||
char *s = opts;
|
||||
char *d = s;
|
||||
int end = 0;
|
||||
|
||||
while (!end) {
|
||||
if (*s == '\0')
|
||||
end = 1;
|
||||
if (*s == ',' || end) {
|
||||
int res;
|
||||
while (!end) {
|
||||
if (*s == '\0')
|
||||
end = 1;
|
||||
if (*s == ',' || end) {
|
||||
int res;
|
||||
|
||||
*d = '\0';
|
||||
res = process_gopt(ctx, opts, 1);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
d = opts;
|
||||
} else {
|
||||
if (s[0] == '\\' && s[1] != '\0') {
|
||||
s++;
|
||||
if (s[0] >= '0' && s[0] <= '3' &&
|
||||
s[1] >= '0' && s[1] <= '7' &&
|
||||
s[2] >= '0' && s[2] <= '7') {
|
||||
*d++ = (s[0] - '0') * 0100 +
|
||||
(s[1] - '0') * 0010 +
|
||||
(s[2] - '0');
|
||||
s += 2;
|
||||
} else {
|
||||
*d++ = *s;
|
||||
}
|
||||
} else {
|
||||
*d++ = *s;
|
||||
}
|
||||
}
|
||||
s++;
|
||||
}
|
||||
*d = '\0';
|
||||
res = process_gopt(ctx, opts, 1);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
d = opts;
|
||||
} else {
|
||||
if (s[0] == '\\' && s[1] != '\0') {
|
||||
s++;
|
||||
if (s[0] >= '0' && s[0] <= '3' &&
|
||||
s[1] >= '0' && s[1] <= '7' &&
|
||||
s[2] >= '0' && s[2] <= '7') {
|
||||
*d++ = (s[0] - '0') * 0100 +
|
||||
(s[1] - '0') * 0010 +
|
||||
(s[2] - '0');
|
||||
s += 2;
|
||||
} else {
|
||||
*d++ = *s;
|
||||
}
|
||||
} else {
|
||||
*d++ = *s;
|
||||
}
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
|
||||
{
|
||||
int res;
|
||||
char *copy = strdup(opts);
|
||||
int res;
|
||||
char *copy = strdup(opts);
|
||||
|
||||
if (!copy) {
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
res = process_real_option_group(ctx, copy);
|
||||
free(copy);
|
||||
return res;
|
||||
if (!copy) {
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
res = process_real_option_group(ctx, copy);
|
||||
free(copy);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_one(struct fuse_opt_context *ctx, const char *arg)
|
||||
{
|
||||
if (ctx->nonopt || arg[0] != '-')
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
|
||||
else if (arg[1] == 'o') {
|
||||
if (arg[2])
|
||||
return process_option_group(ctx, arg + 2);
|
||||
else {
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
if (ctx->nonopt || arg[0] != '-')
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
|
||||
else if (arg[1] == 'o') {
|
||||
if (arg[2])
|
||||
return process_option_group(ctx, arg + 2);
|
||||
else {
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
return process_option_group(ctx,
|
||||
ctx->argv[ctx->argctr]);
|
||||
}
|
||||
} else if (arg[1] == '-' && !arg[2]) {
|
||||
if (add_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
ctx->nonopt = ctx->outargs.argc;
|
||||
return 0;
|
||||
} else
|
||||
return process_gopt(ctx, arg, 0);
|
||||
return process_option_group(ctx,
|
||||
ctx->argv[ctx->argctr]);
|
||||
}
|
||||
} else if (arg[1] == '-' && !arg[2]) {
|
||||
if (add_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
ctx->nonopt = ctx->outargs.argc;
|
||||
return 0;
|
||||
} else
|
||||
return process_gopt(ctx, arg, 0);
|
||||
}
|
||||
|
||||
static int opt_parse(struct fuse_opt_context *ctx)
|
||||
{
|
||||
if (ctx->argc) {
|
||||
if (add_arg(ctx, ctx->argv[0]) == -1)
|
||||
return -1;
|
||||
}
|
||||
if (ctx->argc) {
|
||||
if (add_arg(ctx, ctx->argv[0]) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
|
||||
if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
|
||||
return -1;
|
||||
for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
|
||||
if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
|
||||
return -1;
|
||||
|
||||
if (ctx->opts) {
|
||||
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
|
||||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
|
||||
return -1;
|
||||
}
|
||||
if (ctx->opts) {
|
||||
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
|
||||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If option separator ("--") is the last argument, remove it */
|
||||
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
|
||||
strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
|
||||
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
|
||||
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
|
||||
}
|
||||
/* If option separator ("--") is the last argument, remove it */
|
||||
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
|
||||
strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
|
||||
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
|
||||
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_parse(struct fuse_args *args, void *data,
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc)
|
||||
{
|
||||
int res;
|
||||
struct fuse_opt_context ctx = {
|
||||
.data = data,
|
||||
.opt = opts,
|
||||
.proc = proc,
|
||||
};
|
||||
int res;
|
||||
struct fuse_opt_context ctx = {
|
||||
.data = data,
|
||||
.opt = opts,
|
||||
.proc = proc,
|
||||
};
|
||||
|
||||
if (!args || !args->argv || !args->argc)
|
||||
return 0;
|
||||
if (!args || !args->argv || !args->argc)
|
||||
return 0;
|
||||
|
||||
ctx.argc = args->argc;
|
||||
ctx.argv = args->argv;
|
||||
ctx.argc = args->argc;
|
||||
ctx.argv = args->argv;
|
||||
|
||||
res = opt_parse(&ctx);
|
||||
if (res != -1) {
|
||||
struct fuse_args tmp = *args;
|
||||
*args = ctx.outargs;
|
||||
ctx.outargs = tmp;
|
||||
}
|
||||
free(ctx.opts);
|
||||
fuse_opt_free_args(&ctx.outargs);
|
||||
return res;
|
||||
res = opt_parse(&ctx);
|
||||
if (res != -1) {
|
||||
struct fuse_args tmp = *args;
|
||||
*args = ctx.outargs;
|
||||
ctx.outargs = tmp;
|
||||
}
|
||||
free(ctx.opts);
|
||||
fuse_opt_free_args(&ctx.outargs);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* This symbol version was mistakenly added to the version script */
|
||||
FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");
|
||||
|
||||
+94
-117
@@ -8,8 +8,6 @@
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_common_compat.h"
|
||||
#include "fuse_lowlevel_compat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -17,224 +15,203 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
struct fuse_chan {
|
||||
struct fuse_chan_ops op;
|
||||
|
||||
struct fuse_session *se;
|
||||
|
||||
int fd;
|
||||
|
||||
size_t bufsize;
|
||||
|
||||
void *data;
|
||||
|
||||
int compat;
|
||||
struct fuse_chan
|
||||
{
|
||||
struct fuse_chan_ops op;
|
||||
struct fuse_session *se;
|
||||
int fd;
|
||||
size_t bufsize;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
|
||||
{
|
||||
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
|
||||
if (se == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate session\n");
|
||||
return NULL;
|
||||
}
|
||||
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
|
||||
if (se == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate session\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(se, 0, sizeof(*se));
|
||||
se->op = *op;
|
||||
se->data = data;
|
||||
memset(se, 0, sizeof(*se));
|
||||
se->op = *op;
|
||||
se->data = data;
|
||||
|
||||
return se;
|
||||
return se;
|
||||
}
|
||||
|
||||
void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch)
|
||||
{
|
||||
assert(se->ch == NULL);
|
||||
assert(ch->se == NULL);
|
||||
se->ch = ch;
|
||||
ch->se = se;
|
||||
assert(se->ch == NULL);
|
||||
assert(ch->se == NULL);
|
||||
se->ch = ch;
|
||||
ch->se = se;
|
||||
}
|
||||
|
||||
void fuse_session_remove_chan(struct fuse_chan *ch)
|
||||
{
|
||||
struct fuse_session *se = ch->se;
|
||||
if (se) {
|
||||
assert(se->ch == ch);
|
||||
se->ch = NULL;
|
||||
ch->se = NULL;
|
||||
}
|
||||
struct fuse_session *se = ch->se;
|
||||
if (se) {
|
||||
assert(se->ch == ch);
|
||||
se->ch = NULL;
|
||||
ch->se = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_session_next_chan(struct fuse_session *se,
|
||||
struct fuse_chan *ch)
|
||||
{
|
||||
assert(ch == NULL || ch == se->ch);
|
||||
if (ch == NULL)
|
||||
return se->ch;
|
||||
else
|
||||
return NULL;
|
||||
assert(ch == NULL || ch == se->ch);
|
||||
if (ch == NULL)
|
||||
return se->ch;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fuse_session_process(struct fuse_session *se, const char *buf, size_t len,
|
||||
struct fuse_chan *ch)
|
||||
{
|
||||
se->op.process(se->data, buf, len, ch);
|
||||
se->op.process(se->data, buf, len, ch);
|
||||
}
|
||||
|
||||
void fuse_session_process_buf(struct fuse_session *se,
|
||||
const struct fuse_buf *buf, struct fuse_chan *ch)
|
||||
{
|
||||
if (se->process_buf) {
|
||||
se->process_buf(se->data, buf, ch);
|
||||
} else {
|
||||
assert(!(buf->flags & FUSE_BUF_IS_FD));
|
||||
fuse_session_process(se->data, buf->mem, buf->size, ch);
|
||||
}
|
||||
if (se->process_buf) {
|
||||
se->process_buf(se->data, buf, ch);
|
||||
} else {
|
||||
assert(!(buf->flags & FUSE_BUF_IS_FD));
|
||||
fuse_session_process(se->data, buf->mem, buf->size, ch);
|
||||
}
|
||||
}
|
||||
|
||||
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
|
||||
struct fuse_chan **chp)
|
||||
{
|
||||
int res;
|
||||
int res;
|
||||
|
||||
if (se->receive_buf) {
|
||||
res = se->receive_buf(se, buf, chp);
|
||||
} else {
|
||||
res = fuse_chan_recv(chp, buf->mem, buf->size);
|
||||
if (res > 0)
|
||||
buf->size = res;
|
||||
}
|
||||
if (se->receive_buf) {
|
||||
res = se->receive_buf(se, buf, chp);
|
||||
} else {
|
||||
res = fuse_chan_recv(chp, buf->mem, buf->size);
|
||||
if (res > 0)
|
||||
buf->size = res;
|
||||
}
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void fuse_session_destroy(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.destroy)
|
||||
se->op.destroy(se->data);
|
||||
if (se->ch != NULL)
|
||||
fuse_chan_destroy(se->ch);
|
||||
free(se);
|
||||
if (se->op.destroy)
|
||||
se->op.destroy(se->data);
|
||||
if (se->ch != NULL)
|
||||
fuse_chan_destroy(se->ch);
|
||||
free(se);
|
||||
}
|
||||
|
||||
void fuse_session_exit(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 1);
|
||||
se->exited = 1;
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 1);
|
||||
se->exited = 1;
|
||||
}
|
||||
|
||||
void fuse_session_reset(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 0);
|
||||
se->exited = 0;
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 0);
|
||||
se->exited = 0;
|
||||
}
|
||||
|
||||
int fuse_session_exited(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exited)
|
||||
return se->op.exited(se->data);
|
||||
else
|
||||
return se->exited;
|
||||
if (se->op.exited)
|
||||
return se->op.exited(se->data);
|
||||
else
|
||||
return se->exited;
|
||||
}
|
||||
|
||||
void *fuse_session_data(struct fuse_session *se)
|
||||
{
|
||||
return se->data;
|
||||
return se->data;
|
||||
}
|
||||
|
||||
static struct fuse_chan *fuse_chan_new_common(struct fuse_chan_ops *op, int fd,
|
||||
size_t bufsize, void *data,
|
||||
int compat)
|
||||
size_t bufsize, void *data)
|
||||
{
|
||||
struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
|
||||
if (ch == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate channel\n");
|
||||
return NULL;
|
||||
}
|
||||
struct fuse_chan *ch = (struct fuse_chan *) malloc(sizeof(*ch));
|
||||
if (ch == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate channel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(ch, 0, sizeof(*ch));
|
||||
ch->op = *op;
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
ch->data = data;
|
||||
ch->compat = compat;
|
||||
memset(ch, 0, sizeof(*ch));
|
||||
ch->op = *op;
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
ch->data = data;
|
||||
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd,
|
||||
size_t bufsize, void *data)
|
||||
{
|
||||
return fuse_chan_new_common(op, fd, bufsize, data, 0);
|
||||
}
|
||||
|
||||
struct fuse_chan *fuse_chan_new_compat24(struct fuse_chan_ops_compat24 *op,
|
||||
int fd, size_t bufsize, void *data)
|
||||
{
|
||||
return fuse_chan_new_common((struct fuse_chan_ops *) op, fd, bufsize,
|
||||
data, 24);
|
||||
return fuse_chan_new_common(op, fd, bufsize, data);
|
||||
}
|
||||
|
||||
int fuse_chan_fd(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->fd;
|
||||
return ch->fd;
|
||||
}
|
||||
|
||||
int fuse_chan_clearfd(struct fuse_chan *ch)
|
||||
{
|
||||
int fd = ch->fd;
|
||||
ch->fd = -1;
|
||||
return fd;
|
||||
int fd = ch->fd;
|
||||
ch->fd = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
size_t fuse_chan_bufsize(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->bufsize;
|
||||
return ch->bufsize;
|
||||
}
|
||||
|
||||
void *fuse_chan_data(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->data;
|
||||
return ch->data;
|
||||
}
|
||||
|
||||
struct fuse_session *fuse_chan_session(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->se;
|
||||
return ch->se;
|
||||
}
|
||||
|
||||
int fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t size)
|
||||
{
|
||||
struct fuse_chan *ch = *chp;
|
||||
if (ch->compat)
|
||||
return ((struct fuse_chan_ops_compat24 *) &ch->op)
|
||||
->receive(ch, buf, size);
|
||||
else
|
||||
return ch->op.receive(chp, buf, size);
|
||||
struct fuse_chan *ch = *chp;
|
||||
|
||||
return ch->op.receive(chp, buf, size);
|
||||
}
|
||||
|
||||
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
|
||||
{
|
||||
int res;
|
||||
int res;
|
||||
|
||||
res = fuse_chan_recv(&ch, buf, size);
|
||||
return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
|
||||
res = fuse_chan_recv(&ch, buf, size);
|
||||
return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
|
||||
}
|
||||
|
||||
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
|
||||
{
|
||||
return ch->op.send(ch, iov, count);
|
||||
return ch->op.send(ch, iov, count);
|
||||
}
|
||||
|
||||
void fuse_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
fuse_session_remove_chan(ch);
|
||||
if (ch->op.destroy)
|
||||
ch->op.destroy(ch);
|
||||
free(ch);
|
||||
fuse_session_remove_chan(ch);
|
||||
if (ch->op.destroy)
|
||||
ch->op.destroy(ch);
|
||||
free(ch);
|
||||
}
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
|
||||
#endif
|
||||
|
||||
+35
-36
@@ -16,57 +16,56 @@ static struct fuse_session *fuse_instance;
|
||||
|
||||
static void exit_handler(int sig)
|
||||
{
|
||||
(void) sig;
|
||||
if (fuse_instance)
|
||||
fuse_session_exit(fuse_instance);
|
||||
(void) sig;
|
||||
if (fuse_instance)
|
||||
fuse_session_exit(fuse_instance);
|
||||
}
|
||||
|
||||
static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct sigaction old_sa;
|
||||
struct sigaction sa;
|
||||
struct sigaction old_sa;
|
||||
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = remove ? SIG_DFL : handler;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = remove ? SIG_DFL : handler;
|
||||
sigemptyset(&(sa.sa_mask));
|
||||
sa.sa_flags = 0;
|
||||
|
||||
if (sigaction(sig, NULL, &old_sa) == -1) {
|
||||
perror("fuse: cannot get old signal handler");
|
||||
return -1;
|
||||
}
|
||||
if (sigaction(sig, NULL, &old_sa) == -1) {
|
||||
perror("fuse: cannot get old signal handler");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
|
||||
sigaction(sig, &sa, NULL) == -1) {
|
||||
perror("fuse: cannot set signal handler");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
if (old_sa.sa_handler == (remove ? handler : SIG_DFL) &&
|
||||
sigaction(sig, &sa, NULL) == -1) {
|
||||
perror("fuse: cannot set signal handler");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_set_signal_handlers(struct fuse_session *se)
|
||||
{
|
||||
if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
|
||||
set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
|
||||
set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
|
||||
set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1)
|
||||
return -1;
|
||||
if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 ||
|
||||
set_one_signal_handler(SIGINT, exit_handler, 0) == -1 ||
|
||||
set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 ||
|
||||
set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1)
|
||||
return -1;
|
||||
|
||||
fuse_instance = se;
|
||||
return 0;
|
||||
fuse_instance = se;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fuse_remove_signal_handlers(struct fuse_session *se)
|
||||
{
|
||||
if (fuse_instance != se)
|
||||
fprintf(stderr,
|
||||
"fuse: fuse_remove_signal_handlers: unknown session\n");
|
||||
else
|
||||
fuse_instance = NULL;
|
||||
if (fuse_instance != se)
|
||||
fprintf(stderr,
|
||||
"fuse: fuse_remove_signal_handlers: unknown session\n");
|
||||
else
|
||||
fuse_instance = NULL;
|
||||
|
||||
set_one_signal_handler(SIGHUP, exit_handler, 1);
|
||||
set_one_signal_handler(SIGINT, exit_handler, 1);
|
||||
set_one_signal_handler(SIGTERM, exit_handler, 1);
|
||||
set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
|
||||
set_one_signal_handler(SIGHUP, exit_handler, 1);
|
||||
set_one_signal_handler(SIGINT, exit_handler, 1);
|
||||
set_one_signal_handler(SIGTERM, exit_handler, 1);
|
||||
set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
FUSE_2.2 {
|
||||
global:
|
||||
fuse_destroy;
|
||||
fuse_exit;
|
||||
fuse_exited;
|
||||
fuse_invalidate;
|
||||
fuse_is_lib_option;
|
||||
fuse_loop;
|
||||
fuse_loop_mt;
|
||||
fuse_loop_mt_proc;
|
||||
fuse_main;
|
||||
fuse_main_compat1;
|
||||
fuse_main_compat2;
|
||||
fuse_mount_compat1;
|
||||
fuse_new_compat1;
|
||||
fuse_new_compat2;
|
||||
fuse_process_cmd;
|
||||
fuse_read_cmd;
|
||||
fuse_set_getcontext_func;
|
||||
fuse_setup_compat2;
|
||||
};
|
||||
|
||||
FUSE_2.4 {
|
||||
global:
|
||||
fuse_add_dirent;
|
||||
fuse_chan_bufsize;
|
||||
fuse_chan_data;
|
||||
fuse_chan_destroy;
|
||||
fuse_chan_fd;
|
||||
fuse_chan_receive;
|
||||
fuse_chan_send;
|
||||
fuse_chan_session;
|
||||
fuse_dirent_size;
|
||||
fuse_kern_chan_new;
|
||||
fuse_lowlevel_is_lib_option;
|
||||
fuse_reply_attr;
|
||||
fuse_reply_buf;
|
||||
fuse_reply_entry;
|
||||
fuse_reply_err;
|
||||
fuse_reply_none;
|
||||
fuse_reply_readlink;
|
||||
fuse_reply_write;
|
||||
fuse_reply_xattr;
|
||||
fuse_req_userdata;
|
||||
fuse_session_add_chan;
|
||||
fuse_session_destroy;
|
||||
fuse_session_exit;
|
||||
fuse_session_exited;
|
||||
fuse_session_loop;
|
||||
fuse_session_loop_mt;
|
||||
fuse_session_new;
|
||||
fuse_session_next_chan;
|
||||
fuse_session_process;
|
||||
fuse_session_reset;
|
||||
} FUSE_2.2;
|
||||
|
||||
FUSE_2.5 {
|
||||
global:
|
||||
fuse_lowlevel_new_compat;
|
||||
fuse_main_real_compat22;
|
||||
fuse_mount_compat22;
|
||||
fuse_new_compat22;
|
||||
fuse_opt_parse;
|
||||
fuse_opt_add_opt;
|
||||
fuse_opt_add_arg;
|
||||
fuse_opt_free_args;
|
||||
fuse_opt_match;
|
||||
fuse_parse_cmdline;
|
||||
fuse_remove_signal_handlers;
|
||||
fuse_reply_create;
|
||||
fuse_reply_open;
|
||||
fuse_reply_open_compat;
|
||||
fuse_reply_statfs;
|
||||
fuse_reply_statfs_compat;
|
||||
fuse_setup_compat22;
|
||||
fuse_set_signal_handlers;
|
||||
} FUSE_2.4;
|
||||
|
||||
FUSE_2.6 {
|
||||
global:
|
||||
fuse_add_direntry;
|
||||
fuse_chan_new;
|
||||
fuse_chan_new_compat24;
|
||||
fuse_chan_recv;
|
||||
fuse_daemonize;
|
||||
fuse_get_session;
|
||||
fuse_interrupted;
|
||||
fuse_lowlevel_new;
|
||||
fuse_lowlevel_new_compat25;
|
||||
fuse_main_real;
|
||||
fuse_main_real_compat25;
|
||||
fuse_mount;
|
||||
fuse_mount_compat25;
|
||||
fuse_new;
|
||||
fuse_new_compat25;
|
||||
fuse_opt_insert_arg;
|
||||
fuse_reply_lock;
|
||||
fuse_req_interrupt_func;
|
||||
fuse_req_interrupted;
|
||||
fuse_session_remove_chan;
|
||||
fuse_setup;
|
||||
fuse_setup_compat25;
|
||||
fuse_teardown;
|
||||
fuse_teardown_compat22;
|
||||
fuse_unmount;
|
||||
fuse_unmount_compat22;
|
||||
} FUSE_2.5;
|
||||
|
||||
FUSE_2.7 {
|
||||
global:
|
||||
fuse_fs_access;
|
||||
fuse_fs_bmap;
|
||||
fuse_fs_chmod;
|
||||
fuse_fs_chown;
|
||||
fuse_fs_create;
|
||||
fuse_fs_destroy;
|
||||
fuse_fs_fgetattr;
|
||||
fuse_fs_flush;
|
||||
fuse_fs_fsync;
|
||||
fuse_fs_fsyncdir;
|
||||
fuse_fs_ftruncate;
|
||||
fuse_fs_getattr;
|
||||
fuse_fs_getxattr;
|
||||
fuse_fs_init;
|
||||
fuse_fs_link;
|
||||
fuse_fs_listxattr;
|
||||
fuse_fs_lock;
|
||||
fuse_fs_mkdir;
|
||||
fuse_fs_mknod;
|
||||
fuse_fs_new;
|
||||
fuse_fs_open;
|
||||
fuse_fs_opendir;
|
||||
fuse_fs_read;
|
||||
fuse_fs_readdir;
|
||||
fuse_fs_readlink;
|
||||
fuse_fs_release;
|
||||
fuse_fs_releasedir;
|
||||
fuse_fs_removexattr;
|
||||
fuse_fs_rename;
|
||||
fuse_fs_rmdir;
|
||||
fuse_fs_setxattr;
|
||||
fuse_fs_statfs;
|
||||
fuse_fs_symlink;
|
||||
fuse_fs_truncate;
|
||||
fuse_fs_unlink;
|
||||
fuse_fs_utimens;
|
||||
fuse_fs_write;
|
||||
fuse_register_module;
|
||||
fuse_reply_iov;
|
||||
fuse_version;
|
||||
} FUSE_2.6;
|
||||
|
||||
FUSE_2.7.5 {
|
||||
global:
|
||||
fuse_reply_bmap;
|
||||
} FUSE_2.7;
|
||||
|
||||
FUSE_2.8 {
|
||||
global:
|
||||
cuse_lowlevel_new;
|
||||
cuse_lowlevel_main;
|
||||
cuse_lowlevel_setup;
|
||||
cuse_lowlevel_teardown;
|
||||
fuse_fs_ioctl;
|
||||
fuse_fs_poll;
|
||||
fuse_get_context;
|
||||
fuse_getgroups;
|
||||
fuse_lowlevel_notify_inval_entry;
|
||||
fuse_lowlevel_notify_inval_inode;
|
||||
fuse_lowlevel_notify_poll;
|
||||
fuse_notify_poll;
|
||||
fuse_opt_add_opt_escaped;
|
||||
fuse_pollhandle_destroy;
|
||||
fuse_reply_ioctl;
|
||||
fuse_reply_ioctl_iov;
|
||||
fuse_reply_ioctl_retry;
|
||||
fuse_reply_poll;
|
||||
fuse_req_ctx;
|
||||
fuse_req_getgroups;
|
||||
fuse_session_data;
|
||||
} FUSE_2.7.5;
|
||||
|
||||
FUSE_2.9 {
|
||||
global:
|
||||
fuse_buf_copy;
|
||||
fuse_buf_size;
|
||||
fuse_fs_read_buf;
|
||||
fuse_fs_write_buf;
|
||||
fuse_lowlevel_notify_retrieve;
|
||||
fuse_lowlevel_notify_store;
|
||||
fuse_reply_data;
|
||||
fuse_session_process_buf;
|
||||
fuse_session_receive_buf;
|
||||
fuse_start_cleanup_thread;
|
||||
fuse_stop_cleanup_thread;
|
||||
fuse_clean_cache;
|
||||
fuse_lowlevel_notify_delete;
|
||||
fuse_fs_flock;
|
||||
} FUSE_2.8;
|
||||
|
||||
FUSE_2.9.1 {
|
||||
global:
|
||||
fuse_fs_fallocate;
|
||||
|
||||
local:
|
||||
*;
|
||||
} FUSE_2.9;
|
||||
+190
-299
@@ -11,7 +11,6 @@
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_common_compat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -23,14 +22,13 @@
|
||||
#include <sys/param.h>
|
||||
|
||||
enum {
|
||||
KEY_HELP,
|
||||
KEY_HELP_NOHEADER,
|
||||
KEY_VERSION,
|
||||
KEY_HELP,
|
||||
KEY_HELP_NOHEADER,
|
||||
KEY_VERSION,
|
||||
};
|
||||
|
||||
struct helper_opts
|
||||
{
|
||||
int singlethread;
|
||||
int foreground;
|
||||
int nodefault_subtype;
|
||||
char *mountpoint;
|
||||
@@ -42,115 +40,112 @@ 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("-s", singlethread),
|
||||
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
|
||||
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
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),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s mountpoint [options]\n\n", progname);
|
||||
fprintf(stderr,
|
||||
"general options:\n"
|
||||
" -o opt,[opt...] mount options\n"
|
||||
" -h --help print help\n"
|
||||
" -V --version print version\n"
|
||||
"\n");
|
||||
fprintf(stderr,
|
||||
"usage: %s mountpoint [options]\n\n", progname);
|
||||
fprintf(stderr,
|
||||
"general options:\n"
|
||||
" -o opt,[opt...] mount options\n"
|
||||
" -h --help print help\n"
|
||||
" -V --version print version\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void helper_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"FUSE options:\n"
|
||||
" -d -o debug enable debug output (implies -f)\n"
|
||||
" -f foreground operation\n"
|
||||
" -s disable multi-threaded operation\n"
|
||||
"\n"
|
||||
);
|
||||
fprintf(stderr,
|
||||
"FUSE options:\n"
|
||||
" -d -o debug enable debug output (implies -f)\n"
|
||||
" -f foreground operation\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void helper_version(void)
|
||||
{
|
||||
fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
|
||||
fprintf(stderr, "FUSE library version: %s\n", PACKAGE_VERSION);
|
||||
}
|
||||
|
||||
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct helper_opts *hopts = data;
|
||||
struct helper_opts *hopts = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_HELP:
|
||||
usage(outargs->argv[0]);
|
||||
/* fall through */
|
||||
switch (key) {
|
||||
case KEY_HELP:
|
||||
usage(outargs->argv[0]);
|
||||
/* fall through */
|
||||
|
||||
case KEY_HELP_NOHEADER:
|
||||
helper_help();
|
||||
return fuse_opt_add_arg(outargs, "-h");
|
||||
case KEY_HELP_NOHEADER:
|
||||
helper_help();
|
||||
return fuse_opt_add_arg(outargs, "-h");
|
||||
|
||||
case KEY_VERSION:
|
||||
helper_version();
|
||||
return 1;
|
||||
case KEY_VERSION:
|
||||
helper_version();
|
||||
return 1;
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (!hopts->mountpoint) {
|
||||
char mountpoint[PATH_MAX];
|
||||
if (realpath(arg, mountpoint) == NULL) {
|
||||
fprintf(stderr,
|
||||
"fuse: bad mount point `%s': %s\n",
|
||||
arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
|
||||
} else {
|
||||
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (!hopts->mountpoint) {
|
||||
char mountpoint[PATH_MAX];
|
||||
if (realpath(arg, mountpoint) == NULL) {
|
||||
fprintf(stderr,
|
||||
"fuse: bad mount point `%s': %s\n",
|
||||
arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
|
||||
} else {
|
||||
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_default_subtype(const char *progname, struct fuse_args *args)
|
||||
{
|
||||
int res;
|
||||
char *subtype_opt;
|
||||
const char *basename = strrchr(progname, '/');
|
||||
if (basename == NULL)
|
||||
basename = progname;
|
||||
else if (basename[1] != '\0')
|
||||
basename++;
|
||||
int res;
|
||||
char *subtype_opt;
|
||||
const char *basename = strrchr(progname, '/');
|
||||
if (basename == NULL)
|
||||
basename = progname;
|
||||
else if (basename[1] != '\0')
|
||||
basename++;
|
||||
|
||||
subtype_opt = (char *) malloc(strlen(basename) + 64);
|
||||
if (subtype_opt == NULL) {
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(subtype_opt, "-osubtype=%s", basename);
|
||||
res = fuse_opt_add_arg(args, subtype_opt);
|
||||
free(subtype_opt);
|
||||
return res;
|
||||
subtype_opt = (char *) malloc(strlen(basename) + 64);
|
||||
if (subtype_opt == NULL) {
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
sprintf(subtype_opt, "-osubtype=%s", basename);
|
||||
res = fuse_opt_add_arg(args, subtype_opt);
|
||||
free(subtype_opt);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_parse_cmdline(struct fuse_args *args_,
|
||||
char **mountpoint_,
|
||||
int *multithreaded_,
|
||||
int *foreground_)
|
||||
{
|
||||
int res;
|
||||
@@ -177,8 +172,6 @@ fuse_parse_cmdline(struct fuse_args *args_,
|
||||
else
|
||||
free(hopts.mountpoint);
|
||||
|
||||
if(multithreaded_)
|
||||
*multithreaded_ = !hopts.singlethread;
|
||||
if(foreground_)
|
||||
*foreground_ = hopts.foreground;
|
||||
|
||||
@@ -191,54 +184,54 @@ fuse_parse_cmdline(struct fuse_args *args_,
|
||||
|
||||
int fuse_daemonize(int foreground)
|
||||
{
|
||||
if (!foreground) {
|
||||
int nullfd;
|
||||
int waiter[2];
|
||||
char completed;
|
||||
if (!foreground) {
|
||||
int nullfd;
|
||||
int waiter[2];
|
||||
char completed;
|
||||
|
||||
if (pipe(waiter)) {
|
||||
perror("fuse_daemonize: pipe");
|
||||
return -1;
|
||||
}
|
||||
if (pipe(waiter)) {
|
||||
perror("fuse_daemonize: pipe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* demonize current process by forking it and killing the
|
||||
* parent. This makes current process as a child of 'init'.
|
||||
*/
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
perror("fuse_daemonize: fork");
|
||||
return -1;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
read(waiter[0], &completed, sizeof(completed));
|
||||
_exit(0);
|
||||
}
|
||||
/*
|
||||
* demonize current process by forking it and killing the
|
||||
* parent. This makes current process as a child of 'init'.
|
||||
*/
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
perror("fuse_daemonize: fork");
|
||||
return -1;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
read(waiter[0], &completed, sizeof(completed));
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (setsid() == -1) {
|
||||
perror("fuse_daemonize: setsid");
|
||||
return -1;
|
||||
}
|
||||
if (setsid() == -1) {
|
||||
perror("fuse_daemonize: setsid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void) chdir("/");
|
||||
(void) chdir("/");
|
||||
|
||||
nullfd = open("/dev/null", O_RDWR, 0);
|
||||
if (nullfd != -1) {
|
||||
(void) dup2(nullfd, 0);
|
||||
(void) dup2(nullfd, 1);
|
||||
(void) dup2(nullfd, 2);
|
||||
if (nullfd > 2)
|
||||
close(nullfd);
|
||||
}
|
||||
nullfd = open("/dev/null", O_RDWR, 0);
|
||||
if (nullfd != -1) {
|
||||
(void) dup2(nullfd, 0);
|
||||
(void) dup2(nullfd, 1);
|
||||
(void) dup2(nullfd, 2);
|
||||
if (nullfd > 2)
|
||||
close(nullfd);
|
||||
}
|
||||
|
||||
/* Propagate completion of daemon initializatation */
|
||||
completed = 1;
|
||||
write(waiter[1], &completed, sizeof(completed));
|
||||
close(waiter[0]);
|
||||
close(waiter[1]);
|
||||
}
|
||||
return 0;
|
||||
/* Propagate completion of daemon initializatation */
|
||||
completed = 1;
|
||||
write(waiter[1], &completed, sizeof(completed));
|
||||
close(waiter[0]);
|
||||
close(waiter[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
@@ -260,7 +253,7 @@ fuse_mount_common(const char *mountpoint_,
|
||||
close(fd);
|
||||
} while(fd >= 0 && fd <= 2);
|
||||
|
||||
fd = fuse_mount_compat25(mountpoint_, args_);
|
||||
fd = fuse_kern_mount(mountpoint_,args_);
|
||||
if(fd == -1)
|
||||
return NULL;
|
||||
|
||||
@@ -280,232 +273,130 @@ fuse_mount(const char *mountpoint_,
|
||||
|
||||
static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
|
||||
{
|
||||
if (mountpoint) {
|
||||
int fd = ch ? fuse_chan_clearfd(ch) : -1;
|
||||
fuse_kern_unmount(mountpoint, fd);
|
||||
if (ch)
|
||||
fuse_chan_destroy(ch);
|
||||
}
|
||||
if (mountpoint) {
|
||||
int fd = ch ? fuse_chan_clearfd(ch) : -1;
|
||||
fuse_kern_unmount(mountpoint, fd);
|
||||
if (ch)
|
||||
fuse_chan_destroy(ch);
|
||||
}
|
||||
}
|
||||
|
||||
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
|
||||
{
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
}
|
||||
|
||||
struct fuse *fuse_setup_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *multithreaded,
|
||||
int *fd,
|
||||
void *user_data,
|
||||
int compat)
|
||||
void *user_data)
|
||||
{
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
struct fuse_chan *ch;
|
||||
struct fuse *fuse;
|
||||
int foreground;
|
||||
int res;
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
struct fuse_chan *ch;
|
||||
struct fuse *fuse;
|
||||
int foreground;
|
||||
int res;
|
||||
|
||||
res = fuse_parse_cmdline(&args, mountpoint, multithreaded, &foreground);
|
||||
if (res == -1)
|
||||
return NULL;
|
||||
res = fuse_parse_cmdline(&args, mountpoint, &foreground);
|
||||
if (res == -1)
|
||||
return NULL;
|
||||
|
||||
ch = fuse_mount_common(*mountpoint, &args);
|
||||
if (!ch) {
|
||||
fuse_opt_free_args(&args);
|
||||
goto err_free;
|
||||
}
|
||||
ch = fuse_mount_common(*mountpoint, &args);
|
||||
if (!ch) {
|
||||
fuse_opt_free_args(&args);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
fuse = fuse_new_common(ch, &args, op, op_size, user_data, compat);
|
||||
fuse_opt_free_args(&args);
|
||||
if (fuse == NULL)
|
||||
goto err_unmount;
|
||||
fuse = fuse_new_common(ch, &args, op, op_size, user_data);
|
||||
fuse_opt_free_args(&args);
|
||||
if (fuse == NULL)
|
||||
goto err_unmount;
|
||||
|
||||
res = fuse_daemonize(foreground);
|
||||
if (res == -1)
|
||||
goto err_unmount;
|
||||
res = fuse_daemonize(foreground);
|
||||
if (res == -1)
|
||||
goto err_unmount;
|
||||
|
||||
res = fuse_set_signal_handlers(fuse_get_session(fuse));
|
||||
if (res == -1)
|
||||
goto err_unmount;
|
||||
res = fuse_set_signal_handlers(fuse_get_session(fuse));
|
||||
if (res == -1)
|
||||
goto err_unmount;
|
||||
|
||||
if (fd)
|
||||
*fd = fuse_chan_fd(ch);
|
||||
if (fd)
|
||||
*fd = fuse_chan_fd(ch);
|
||||
|
||||
return fuse;
|
||||
return fuse;
|
||||
|
||||
err_unmount:
|
||||
fuse_unmount_common(*mountpoint, ch);
|
||||
if (fuse)
|
||||
fuse_destroy(fuse);
|
||||
err_free:
|
||||
free(*mountpoint);
|
||||
return NULL;
|
||||
err_unmount:
|
||||
fuse_unmount_common(*mountpoint, ch);
|
||||
if (fuse)
|
||||
fuse_destroy(fuse);
|
||||
err_free:
|
||||
free(*mountpoint);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct fuse *fuse_setup(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
char **mountpoint, int *multithreaded, void *user_data)
|
||||
char **mountpoint, void *user_data)
|
||||
{
|
||||
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
|
||||
multithreaded, NULL, user_data, 0);
|
||||
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
|
||||
NULL, user_data);
|
||||
}
|
||||
|
||||
static void fuse_teardown_common(struct fuse *fuse, char *mountpoint)
|
||||
{
|
||||
struct fuse_session *se = fuse_get_session(fuse);
|
||||
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
fuse_destroy(fuse);
|
||||
free(mountpoint);
|
||||
struct fuse_session *se = fuse_get_session(fuse);
|
||||
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
fuse_destroy(fuse);
|
||||
free(mountpoint);
|
||||
}
|
||||
|
||||
void fuse_teardown(struct fuse *fuse, char *mountpoint)
|
||||
{
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
}
|
||||
|
||||
static int fuse_main_common(int argc, char *argv[],
|
||||
const struct fuse_operations *op, size_t op_size,
|
||||
void *user_data, int compat)
|
||||
void *user_data)
|
||||
{
|
||||
struct fuse *fuse;
|
||||
char *mountpoint;
|
||||
int multithreaded;
|
||||
int res;
|
||||
struct fuse *fuse;
|
||||
char *mountpoint;
|
||||
int res;
|
||||
|
||||
fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
|
||||
&multithreaded, NULL, user_data, compat);
|
||||
if (fuse == NULL)
|
||||
return 1;
|
||||
fuse = fuse_setup_common(argc, argv, op, op_size,
|
||||
&mountpoint,
|
||||
NULL, user_data);
|
||||
if (fuse == NULL)
|
||||
return 1;
|
||||
|
||||
if (multithreaded)
|
||||
res = fuse_loop_mt(fuse);
|
||||
else
|
||||
res = fuse_loop(fuse);
|
||||
res = fuse_loop_mt(fuse);
|
||||
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
if (res == -1)
|
||||
return 1;
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
if (res == -1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
|
||||
size_t op_size, void *user_data)
|
||||
{
|
||||
return fuse_main_common(argc, argv, op, op_size, user_data, 0);
|
||||
return fuse_main_common(argc, argv, op, op_size, user_data);
|
||||
}
|
||||
|
||||
#undef fuse_main
|
||||
int fuse_main(void);
|
||||
int fuse_main(void)
|
||||
{
|
||||
fprintf(stderr, "fuse_main(): This function does not exist\n");
|
||||
return -1;
|
||||
fprintf(stderr, "fuse_main(): This function does not exist\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fuse_version(void)
|
||||
{
|
||||
return FUSE_VERSION;
|
||||
return FUSE_VERSION;
|
||||
}
|
||||
|
||||
#include "fuse_compat.h"
|
||||
|
||||
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
|
||||
struct fuse *fuse_setup_compat22(int argc, char *argv[],
|
||||
const struct fuse_operations_compat22 *op,
|
||||
size_t op_size, char **mountpoint,
|
||||
int *multithreaded, int *fd)
|
||||
{
|
||||
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
|
||||
op_size, mountpoint, multithreaded, fd, NULL,
|
||||
22);
|
||||
}
|
||||
|
||||
struct fuse *fuse_setup_compat2(int argc, char *argv[],
|
||||
const struct fuse_operations_compat2 *op,
|
||||
char **mountpoint, int *multithreaded,
|
||||
int *fd)
|
||||
{
|
||||
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
|
||||
sizeof(struct fuse_operations_compat2),
|
||||
mountpoint, multithreaded, fd, NULL, 21);
|
||||
}
|
||||
|
||||
int fuse_main_real_compat22(int argc, char *argv[],
|
||||
const struct fuse_operations_compat22 *op,
|
||||
size_t op_size)
|
||||
{
|
||||
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
|
||||
op_size, NULL, 22);
|
||||
}
|
||||
|
||||
void fuse_main_compat1(int argc, char *argv[],
|
||||
const struct fuse_operations_compat1 *op)
|
||||
{
|
||||
fuse_main_common(argc, argv, (struct fuse_operations *) op,
|
||||
sizeof(struct fuse_operations_compat1), NULL, 11);
|
||||
}
|
||||
|
||||
int fuse_main_compat2(int argc, char *argv[],
|
||||
const struct fuse_operations_compat2 *op)
|
||||
{
|
||||
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
|
||||
sizeof(struct fuse_operations_compat2), NULL,
|
||||
21);
|
||||
}
|
||||
|
||||
int fuse_mount_compat1(const char *mountpoint, const char *args[])
|
||||
{
|
||||
/* just ignore mount args for now */
|
||||
(void) args;
|
||||
return fuse_mount_compat22(mountpoint, NULL);
|
||||
}
|
||||
|
||||
FUSE_SYMVER(".symver fuse_setup_compat2,__fuse_setup@");
|
||||
FUSE_SYMVER(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
|
||||
FUSE_SYMVER(".symver fuse_teardown,__fuse_teardown@");
|
||||
FUSE_SYMVER(".symver fuse_main_compat2,fuse_main@");
|
||||
FUSE_SYMVER(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
|
||||
|
||||
#endif /* __FreeBSD__ || __NetBSD__ */
|
||||
|
||||
|
||||
struct fuse *fuse_setup_compat25(int argc, char *argv[],
|
||||
const struct fuse_operations_compat25 *op,
|
||||
size_t op_size, char **mountpoint,
|
||||
int *multithreaded, int *fd)
|
||||
{
|
||||
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
|
||||
op_size, mountpoint, multithreaded, fd, NULL,
|
||||
25);
|
||||
}
|
||||
|
||||
int fuse_main_real_compat25(int argc, char *argv[],
|
||||
const struct fuse_operations_compat25 *op,
|
||||
size_t op_size)
|
||||
{
|
||||
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
|
||||
op_size, NULL, 25);
|
||||
}
|
||||
|
||||
void fuse_teardown_compat22(struct fuse *fuse, int fd, char *mountpoint)
|
||||
{
|
||||
(void) fd;
|
||||
fuse_teardown_common(fuse, mountpoint);
|
||||
}
|
||||
|
||||
int fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)
|
||||
{
|
||||
return fuse_kern_mount(mountpoint, args);
|
||||
}
|
||||
|
||||
FUSE_SYMVER(".symver fuse_setup_compat25,fuse_setup@FUSE_2.5");
|
||||
FUSE_SYMVER(".symver fuse_teardown_compat22,fuse_teardown@FUSE_2.2");
|
||||
FUSE_SYMVER(".symver fuse_main_real_compat25,fuse_main_real@FUSE_2.5");
|
||||
FUSE_SYMVER(".symver fuse_mount_compat25,fuse_mount@FUSE_2.5");
|
||||
|
||||
+222
-271
@@ -28,364 +28,315 @@
|
||||
#define FUSE_DEV_TRUNK "/dev/fuse"
|
||||
|
||||
enum {
|
||||
KEY_ALLOW_ROOT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
KEY_KERN
|
||||
KEY_ALLOW_ROOT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
KEY_KERN
|
||||
};
|
||||
|
||||
struct mount_opts {
|
||||
int allow_other;
|
||||
int allow_root;
|
||||
int ishelp;
|
||||
char *kernel_opts;
|
||||
int allow_other;
|
||||
int allow_root;
|
||||
int ishelp;
|
||||
char *kernel_opts;
|
||||
};
|
||||
|
||||
#define FUSE_DUAL_OPT_KEY(templ, key) \
|
||||
FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
|
||||
FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
|
||||
|
||||
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),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
/* standard FreeBSD mount options */
|
||||
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("async", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("union", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("force", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("update", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
|
||||
/* options supported under both Linux and FBSD */
|
||||
FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
|
||||
FUSE_OPT_KEY("max_read=", KEY_KERN),
|
||||
FUSE_OPT_KEY("subtype=", KEY_KERN),
|
||||
/* FBSD FUSE specific mount options */
|
||||
FUSE_DUAL_OPT_KEY("private", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
|
||||
FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
|
||||
/* stock FBSD mountopt parsing routine lets anything be negated... */
|
||||
/*
|
||||
* Linux specific mount options, but let just the mount util
|
||||
* handle them
|
||||
*/
|
||||
FUSE_OPT_KEY("fsname=", KEY_KERN),
|
||||
FUSE_OPT_KEY("nonempty", KEY_KERN),
|
||||
FUSE_OPT_KEY("large_read", KEY_KERN),
|
||||
FUSE_OPT_END
|
||||
{ "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),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
/* standard FreeBSD mount options */
|
||||
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("async", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("atime", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("dev", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("exec", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("suid", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("symfollow", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("rdonly", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("sync", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("union", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("userquota", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("groupquota", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("clusterr", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("clusterw", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("suiddir", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("snapshot", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("multilabel", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("acls", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("force", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("update", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("ro", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("rw", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("auto", KEY_KERN),
|
||||
/* options supported under both Linux and FBSD */
|
||||
FUSE_DUAL_OPT_KEY("allow_other", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
|
||||
FUSE_OPT_KEY("max_read=", KEY_KERN),
|
||||
FUSE_OPT_KEY("subtype=", KEY_KERN),
|
||||
/* FBSD FUSE specific mount options */
|
||||
FUSE_DUAL_OPT_KEY("private", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("neglect_shares", KEY_KERN),
|
||||
FUSE_DUAL_OPT_KEY("push_symlinks_in", KEY_KERN),
|
||||
FUSE_OPT_KEY("nosync_unmount", KEY_KERN),
|
||||
/* stock FBSD mountopt parsing routine lets anything be negated... */
|
||||
/*
|
||||
* Linux specific mount options, but let just the mount util
|
||||
* handle them
|
||||
*/
|
||||
FUSE_OPT_KEY("fsname=", KEY_KERN),
|
||||
FUSE_OPT_KEY("nonempty", KEY_KERN),
|
||||
FUSE_OPT_KEY("large_read", KEY_KERN),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void mount_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o allow_root allow access to root\n"
|
||||
);
|
||||
system(FUSERMOUNT_PROG " --help");
|
||||
fputc('\n', stderr);
|
||||
fprintf(stderr,
|
||||
" -o allow_root allow access to root\n"
|
||||
);
|
||||
system(FUSERMOUNT_PROG " --help");
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
static void mount_version(void)
|
||||
{
|
||||
system(FUSERMOUNT_PROG " --version");
|
||||
system(FUSERMOUNT_PROG " --version");
|
||||
}
|
||||
|
||||
static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct mount_opts *mo = data;
|
||||
struct mount_opts *mo = 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;
|
||||
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 */
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
|
||||
case KEY_KERN:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
case KEY_KERN:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void fuse_unmount_compat22(const char *mountpoint)
|
||||
{
|
||||
char dev[128];
|
||||
char *ssc, *umount_cmd;
|
||||
FILE *sf;
|
||||
int rv;
|
||||
char seekscript[] =
|
||||
/* error message is annoying in help output */
|
||||
"exec 2>/dev/null; "
|
||||
"/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
|
||||
"/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
|
||||
" { if ($3 == %d) print $10; }' | "
|
||||
"/usr/bin/sort | "
|
||||
"/usr/bin/uniq | "
|
||||
"/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
|
||||
|
||||
(void) mountpoint;
|
||||
|
||||
/*
|
||||
* If we don't know the fd, we have to resort to the scripted
|
||||
* solution -- iterating over the fd-s is unpractical, as we
|
||||
* don't know how many of open files we have. (This could be
|
||||
* looked up in procfs -- however, that's optional on FBSD; or
|
||||
* read out from the kmem -- however, that's bound to
|
||||
* privileges (in fact, that's what happens when we call the
|
||||
* setgid kmem fstat(1) utility).
|
||||
*/
|
||||
if (asprintf(&ssc, seekscript, getpid()) == -1)
|
||||
return;
|
||||
|
||||
errno = 0;
|
||||
sf = popen(ssc, "r");
|
||||
free(ssc);
|
||||
if (! sf)
|
||||
return;
|
||||
|
||||
fgets(dev, sizeof(dev), sf);
|
||||
rv = pclose(sf);
|
||||
if (rv)
|
||||
return;
|
||||
|
||||
if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
|
||||
return;
|
||||
system(umount_cmd);
|
||||
free(umount_cmd);
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void do_unmount(char *dev, int fd)
|
||||
{
|
||||
char device_path[SPECNAMELEN + 12];
|
||||
const char *argv[4];
|
||||
const char umount_cmd[] = "/sbin/umount";
|
||||
pid_t pid;
|
||||
char device_path[SPECNAMELEN + 12];
|
||||
const char *argv[4];
|
||||
const char umount_cmd[] = "/sbin/umount";
|
||||
pid_t pid;
|
||||
|
||||
snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
|
||||
snprintf(device_path, SPECNAMELEN + 12, _PATH_DEV "%s", dev);
|
||||
|
||||
argv[0] = umount_cmd;
|
||||
argv[1] = "-f";
|
||||
argv[2] = device_path;
|
||||
argv[3] = NULL;
|
||||
argv[0] = umount_cmd;
|
||||
argv[1] = "-f";
|
||||
argv[2] = device_path;
|
||||
argv[3] = NULL;
|
||||
|
||||
pid = fork();
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
return;
|
||||
if (pid == -1)
|
||||
return;
|
||||
|
||||
if (pid == 0) {
|
||||
close(fd);
|
||||
execvp(umount_cmd, (char **)argv);
|
||||
exit(1);
|
||||
}
|
||||
if (pid == 0) {
|
||||
close(fd);
|
||||
execvp(umount_cmd, (char **)argv);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
void fuse_kern_unmount(const char *mountpoint, int fd)
|
||||
{
|
||||
char *ep, dev[128];
|
||||
struct stat sbuf;
|
||||
char *ep, dev[128];
|
||||
struct stat sbuf;
|
||||
|
||||
(void)mountpoint;
|
||||
(void)mountpoint;
|
||||
|
||||
if (fstat(fd, &sbuf) == -1)
|
||||
goto out;
|
||||
if (fstat(fd, &sbuf) == -1)
|
||||
goto out;
|
||||
|
||||
devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
|
||||
devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
|
||||
|
||||
if (strncmp(dev, "fuse", 4))
|
||||
goto out;
|
||||
if (strncmp(dev, "fuse", 4))
|
||||
goto out;
|
||||
|
||||
strtol(dev + 4, &ep, 10);
|
||||
if (*ep != '\0')
|
||||
goto out;
|
||||
strtol(dev + 4, &ep, 10);
|
||||
if (*ep != '\0')
|
||||
goto out;
|
||||
|
||||
do_unmount(dev, fd);
|
||||
do_unmount(dev, fd);
|
||||
|
||||
out:
|
||||
close(fd);
|
||||
out:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Check if kernel is doing init in background */
|
||||
static int init_backgrounded(void)
|
||||
{
|
||||
unsigned ibg, len;
|
||||
unsigned ibg, len;
|
||||
|
||||
len = sizeof(ibg);
|
||||
len = sizeof(ibg);
|
||||
|
||||
if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
|
||||
return 0;
|
||||
if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
|
||||
return 0;
|
||||
|
||||
return ibg;
|
||||
return ibg;
|
||||
}
|
||||
|
||||
|
||||
static int fuse_mount_core(const char *mountpoint, const char *opts)
|
||||
{
|
||||
const char *mountprog = FUSERMOUNT_PROG;
|
||||
int fd;
|
||||
char *fdnam, *dev;
|
||||
pid_t pid, cpid;
|
||||
int status;
|
||||
const char *mountprog = FUSERMOUNT_PROG;
|
||||
int fd;
|
||||
char *fdnam, *dev;
|
||||
pid_t pid, cpid;
|
||||
int status;
|
||||
|
||||
fdnam = getenv("FUSE_DEV_FD");
|
||||
fdnam = getenv("FUSE_DEV_FD");
|
||||
|
||||
if (fdnam) {
|
||||
char *ep;
|
||||
if (fdnam) {
|
||||
char *ep;
|
||||
|
||||
fd = strtol(fdnam, &ep, 10);
|
||||
fd = strtol(fdnam, &ep, 10);
|
||||
|
||||
if (*ep != '\0') {
|
||||
fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
|
||||
return -1;
|
||||
}
|
||||
if (*ep != '\0') {
|
||||
fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
goto mount;
|
||||
}
|
||||
goto mount;
|
||||
}
|
||||
|
||||
dev = getenv("FUSE_DEV_NAME");
|
||||
dev = getenv("FUSE_DEV_NAME");
|
||||
|
||||
if (! dev)
|
||||
dev = (char *)FUSE_DEV_TRUNK;
|
||||
if (! dev)
|
||||
dev = (char *)FUSE_DEV_TRUNK;
|
||||
|
||||
if ((fd = open(dev, O_RDWR)) < 0) {
|
||||
perror("fuse: failed to open fuse device");
|
||||
return -1;
|
||||
}
|
||||
if ((fd = open(dev, O_RDWR)) < 0) {
|
||||
perror("fuse: failed to open fuse device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mount:
|
||||
if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
|
||||
goto out;
|
||||
mount:
|
||||
if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
|
||||
goto out;
|
||||
|
||||
pid = fork();
|
||||
cpid = pid;
|
||||
pid = fork();
|
||||
cpid = pid;
|
||||
|
||||
if (pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
if (! init_backgrounded()) {
|
||||
/*
|
||||
* If init is not backgrounded, we have to
|
||||
* call the mount util backgrounded, to avoid
|
||||
* deadlock.
|
||||
*/
|
||||
if (pid == 0) {
|
||||
if (! init_backgrounded()) {
|
||||
/*
|
||||
* If init is not backgrounded, we have to
|
||||
* call the mount util backgrounded, to avoid
|
||||
* deadlock.
|
||||
*/
|
||||
|
||||
pid = fork();
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (pid == -1) {
|
||||
perror("fuse: fork() failed");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
const char *argv[32];
|
||||
int a = 0;
|
||||
if (pid == 0) {
|
||||
const char *argv[32];
|
||||
int a = 0;
|
||||
|
||||
if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
|
||||
perror("fuse: failed to assemble mount arguments");
|
||||
exit(1);
|
||||
}
|
||||
if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
|
||||
perror("fuse: failed to assemble mount arguments");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
argv[a++] = mountprog;
|
||||
if (opts) {
|
||||
argv[a++] = "-o";
|
||||
argv[a++] = opts;
|
||||
}
|
||||
argv[a++] = fdnam;
|
||||
argv[a++] = mountpoint;
|
||||
argv[a++] = NULL;
|
||||
execvp(mountprog, (char **) argv);
|
||||
perror("fuse: failed to exec mount program");
|
||||
exit(1);
|
||||
}
|
||||
argv[a++] = mountprog;
|
||||
if (opts) {
|
||||
argv[a++] = "-o";
|
||||
argv[a++] = opts;
|
||||
}
|
||||
argv[a++] = fdnam;
|
||||
argv[a++] = mountpoint;
|
||||
argv[a++] = NULL;
|
||||
execvp(mountprog, (char **) argv);
|
||||
perror("fuse: failed to exec mount program");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
|
||||
perror("fuse: failed to mount file system");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
|
||||
perror("fuse: failed to mount file system");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
out:
|
||||
return fd;
|
||||
out:
|
||||
return fd;
|
||||
}
|
||||
|
||||
int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
||||
{
|
||||
struct mount_opts mo;
|
||||
int res = -1;
|
||||
struct mount_opts mo;
|
||||
int res = -1;
|
||||
|
||||
memset(&mo, 0, sizeof(mo));
|
||||
/* mount util should not try to spawn the daemon */
|
||||
setenv("MOUNT_FUSEFS_SAFE", "1", 1);
|
||||
/* to notify the mount util it's called from lib */
|
||||
setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
|
||||
memset(&mo, 0, sizeof(mo));
|
||||
/* mount util should not try to spawn the daemon */
|
||||
setenv("MOUNT_FUSEFS_SAFE", "1", 1);
|
||||
/* to notify the mount util it's called from lib */
|
||||
setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
|
||||
|
||||
if (args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
if (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;
|
||||
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;
|
||||
|
||||
res = fuse_mount_core(mountpoint, mo.kernel_opts);
|
||||
out:
|
||||
free(mo.kernel_opts);
|
||||
return res;
|
||||
res = fuse_mount_core(mountpoint, mo.kernel_opts);
|
||||
out:
|
||||
free(mo.kernel_opts);
|
||||
return res;
|
||||
}
|
||||
|
||||
FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
|
||||
|
||||
+445
-461
File diff suppressed because it is too large
Load Diff
+251
-251
@@ -31,333 +31,333 @@
|
||||
#else
|
||||
static int mtab_needs_update(const char *mnt)
|
||||
{
|
||||
int res;
|
||||
struct stat stbuf;
|
||||
int res;
|
||||
struct stat stbuf;
|
||||
|
||||
/* If mtab is within new mount, don't touch it */
|
||||
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
|
||||
_PATH_MOUNTED[strlen(mnt)] == '/')
|
||||
return 0;
|
||||
/* If mtab is within new mount, don't touch it */
|
||||
if (strncmp(mnt, _PATH_MOUNTED, strlen(mnt)) == 0 &&
|
||||
_PATH_MOUNTED[strlen(mnt)] == '/')
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Skip mtab update if /etc/mtab:
|
||||
*
|
||||
* - doesn't exist,
|
||||
* - is a symlink,
|
||||
* - is on a read-only filesystem.
|
||||
*/
|
||||
res = lstat(_PATH_MOUNTED, &stbuf);
|
||||
if (res == -1) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
} else {
|
||||
uid_t ruid;
|
||||
int err;
|
||||
/*
|
||||
* Skip mtab update if /etc/mtab:
|
||||
*
|
||||
* - doesn't exist,
|
||||
* - is a symlink,
|
||||
* - is on a read-only filesystem.
|
||||
*/
|
||||
res = lstat(_PATH_MOUNTED, &stbuf);
|
||||
if (res == -1) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
} else {
|
||||
uid_t ruid;
|
||||
int err;
|
||||
|
||||
if (S_ISLNK(stbuf.st_mode))
|
||||
return 0;
|
||||
if (S_ISLNK(stbuf.st_mode))
|
||||
return 0;
|
||||
|
||||
ruid = getuid();
|
||||
if (ruid != 0)
|
||||
setreuid(0, -1);
|
||||
ruid = getuid();
|
||||
if (ruid != 0)
|
||||
setreuid(0, -1);
|
||||
|
||||
res = access(_PATH_MOUNTED, W_OK);
|
||||
err = (res == -1) ? errno : 0;
|
||||
if (ruid != 0)
|
||||
setreuid(ruid, -1);
|
||||
res = access(_PATH_MOUNTED, W_OK);
|
||||
err = (res == -1) ? errno : 0;
|
||||
if (ruid != 0)
|
||||
setreuid(ruid, -1);
|
||||
|
||||
if (err == EROFS)
|
||||
return 0;
|
||||
}
|
||||
if (err == EROFS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
#endif /* __NetBSD__ */
|
||||
|
||||
static int add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
|
||||
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
|
||||
"-f", "-t", type, "-o", opts, fsname, mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/mount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
const char *mnt, const char *type, const char *opts)
|
||||
{
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
return add_mount(progname, fsname, mnt, type, opts);
|
||||
return add_mount(progname, fsname, mnt, type, opts);
|
||||
}
|
||||
|
||||
static int exec_umount(const char *progname, const char *rel_mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
if (lazy) {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
"-l", NULL, &env);
|
||||
} else {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
NULL, &env);
|
||||
}
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
if (lazy) {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
"-l", NULL, &env);
|
||||
} else {
|
||||
execle("/bin/umount", "/bin/umount", "-i", rel_mnt,
|
||||
NULL, &env);
|
||||
}
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0) {
|
||||
res = -1;
|
||||
}
|
||||
if (status != 0) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
int fuse_mnt_umount(const char *progname, const char *abs_mnt,
|
||||
const char *rel_mnt, int lazy)
|
||||
{
|
||||
int res;
|
||||
int res;
|
||||
|
||||
if (!mtab_needs_update(abs_mnt)) {
|
||||
res = umount2(rel_mnt, lazy ? 2 : 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n",
|
||||
progname, abs_mnt, strerror(errno));
|
||||
return res;
|
||||
}
|
||||
if (!mtab_needs_update(abs_mnt)) {
|
||||
res = umount2(rel_mnt, lazy ? 2 : 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: failed to unmount %s: %s\n",
|
||||
progname, abs_mnt, strerror(errno));
|
||||
return res;
|
||||
}
|
||||
|
||||
return exec_umount(progname, rel_mnt, lazy);
|
||||
return exec_umount(progname, rel_mnt, lazy);
|
||||
}
|
||||
|
||||
static int remove_mount(const char *progname, const char *mnt)
|
||||
{
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
int res;
|
||||
int status;
|
||||
sigset_t blockmask;
|
||||
sigset_t oldmask;
|
||||
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
sigemptyset(&blockmask);
|
||||
sigaddset(&blockmask, SIGCHLD);
|
||||
res = sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: sigprocmask: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
res = fork();
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
|
||||
goto out_restore;
|
||||
}
|
||||
if (res == 0) {
|
||||
char *env = NULL;
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
|
||||
"--fake", mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
setuid(geteuid());
|
||||
execle("/bin/umount", "/bin/umount", "--no-canonicalize", "-i",
|
||||
"--fake", mnt, NULL, &env);
|
||||
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
|
||||
progname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
res = waitpid(res, &status, 0);
|
||||
if (res == -1)
|
||||
fprintf(stderr, "%s: waitpid: %s\n", progname, strerror(errno));
|
||||
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
if (status != 0)
|
||||
res = -1;
|
||||
|
||||
out_restore:
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return res;
|
||||
}
|
||||
|
||||
int fuse_mnt_remove_mount(const char *progname, const char *mnt)
|
||||
{
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
if (!mtab_needs_update(mnt))
|
||||
return 0;
|
||||
|
||||
return remove_mount(progname, mnt);
|
||||
return remove_mount(progname, mnt);
|
||||
}
|
||||
|
||||
char *fuse_mnt_resolve_path(const char *progname, const char *orig)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char *copy;
|
||||
char *dst;
|
||||
char *end;
|
||||
char *lastcomp;
|
||||
const char *toresolv;
|
||||
char buf[PATH_MAX];
|
||||
char *copy;
|
||||
char *dst;
|
||||
char *end;
|
||||
char *lastcomp;
|
||||
const char *toresolv;
|
||||
|
||||
if (!orig[0]) {
|
||||
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
|
||||
orig);
|
||||
return NULL;
|
||||
}
|
||||
if (!orig[0]) {
|
||||
fprintf(stderr, "%s: invalid mountpoint '%s'\n", progname,
|
||||
orig);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy = strdup(orig);
|
||||
if (copy == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
copy = strdup(orig);
|
||||
if (copy == NULL) {
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toresolv = copy;
|
||||
lastcomp = NULL;
|
||||
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
|
||||
if (end[0] != '/') {
|
||||
char *tmp;
|
||||
end[1] = '\0';
|
||||
tmp = strrchr(copy, '/');
|
||||
if (tmp == NULL) {
|
||||
lastcomp = copy;
|
||||
toresolv = ".";
|
||||
} else {
|
||||
lastcomp = tmp + 1;
|
||||
if (tmp == copy)
|
||||
toresolv = "/";
|
||||
}
|
||||
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
|
||||
lastcomp = NULL;
|
||||
toresolv = copy;
|
||||
}
|
||||
else if (tmp)
|
||||
tmp[0] = '\0';
|
||||
}
|
||||
if (realpath(toresolv, buf) == NULL) {
|
||||
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
|
||||
strerror(errno));
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
if (lastcomp == NULL)
|
||||
dst = strdup(buf);
|
||||
else {
|
||||
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
|
||||
if (dst) {
|
||||
unsigned buflen = strlen(buf);
|
||||
if (buflen && buf[buflen-1] == '/')
|
||||
sprintf(dst, "%s%s", buf, lastcomp);
|
||||
else
|
||||
sprintf(dst, "%s/%s", buf, lastcomp);
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
if (dst == NULL)
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return dst;
|
||||
toresolv = copy;
|
||||
lastcomp = NULL;
|
||||
for (end = copy + strlen(copy) - 1; end > copy && *end == '/'; end --);
|
||||
if (end[0] != '/') {
|
||||
char *tmp;
|
||||
end[1] = '\0';
|
||||
tmp = strrchr(copy, '/');
|
||||
if (tmp == NULL) {
|
||||
lastcomp = copy;
|
||||
toresolv = ".";
|
||||
} else {
|
||||
lastcomp = tmp + 1;
|
||||
if (tmp == copy)
|
||||
toresolv = "/";
|
||||
}
|
||||
if (strcmp(lastcomp, ".") == 0 || strcmp(lastcomp, "..") == 0) {
|
||||
lastcomp = NULL;
|
||||
toresolv = copy;
|
||||
}
|
||||
else if (tmp)
|
||||
tmp[0] = '\0';
|
||||
}
|
||||
if (realpath(toresolv, buf) == NULL) {
|
||||
fprintf(stderr, "%s: bad mount point %s: %s\n", progname, orig,
|
||||
strerror(errno));
|
||||
free(copy);
|
||||
return NULL;
|
||||
}
|
||||
if (lastcomp == NULL)
|
||||
dst = strdup(buf);
|
||||
else {
|
||||
dst = (char *) malloc(strlen(buf) + 1 + strlen(lastcomp) + 1);
|
||||
if (dst) {
|
||||
unsigned buflen = strlen(buf);
|
||||
if (buflen && buf[buflen-1] == '/')
|
||||
sprintf(dst, "%s%s", buf, lastcomp);
|
||||
else
|
||||
sprintf(dst, "%s/%s", buf, lastcomp);
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
if (dst == NULL)
|
||||
fprintf(stderr, "%s: failed to allocate memory\n", progname);
|
||||
return dst;
|
||||
}
|
||||
|
||||
int fuse_mnt_check_empty(const char *progname, const char *mnt,
|
||||
mode_t rootmode, off_t rootsize)
|
||||
{
|
||||
int isempty = 1;
|
||||
int isempty = 1;
|
||||
|
||||
if (S_ISDIR(rootmode)) {
|
||||
struct dirent *ent;
|
||||
DIR *dp = opendir(mnt);
|
||||
if (dp == NULL) {
|
||||
fprintf(stderr,
|
||||
"%s: failed to open mountpoint for reading: %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while ((ent = readdir(dp)) != NULL) {
|
||||
if (strcmp(ent->d_name, ".") != 0 &&
|
||||
strcmp(ent->d_name, "..") != 0) {
|
||||
isempty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
} else if (rootsize)
|
||||
isempty = 0;
|
||||
if (S_ISDIR(rootmode)) {
|
||||
struct dirent *ent;
|
||||
DIR *dp = opendir(mnt);
|
||||
if (dp == NULL) {
|
||||
fprintf(stderr,
|
||||
"%s: failed to open mountpoint for reading: %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while ((ent = readdir(dp)) != NULL) {
|
||||
if (strcmp(ent->d_name, ".") != 0 &&
|
||||
strcmp(ent->d_name, "..") != 0) {
|
||||
isempty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
} else if (rootsize)
|
||||
isempty = 0;
|
||||
|
||||
if (!isempty) {
|
||||
fprintf(stderr, "%s: mountpoint is not empty\n", progname);
|
||||
fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
if (!isempty) {
|
||||
fprintf(stderr, "%s: mountpoint is not empty\n", progname);
|
||||
fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_mnt_check_fuseblk(void)
|
||||
{
|
||||
char buf[256];
|
||||
FILE *f = fopen("/proc/filesystems", "r");
|
||||
if (!f)
|
||||
return 1;
|
||||
char buf[256];
|
||||
FILE *f = fopen("/proc/filesystems", "r");
|
||||
if (!f)
|
||||
return 1;
|
||||
|
||||
while (fgets(buf, sizeof(buf), f))
|
||||
if (strstr(buf, "fuseblk\n")) {
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
while (fgets(buf, sizeof(buf), f))
|
||||
if (strstr(buf, "fuseblk\n")) {
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int fuse_mnt_add_mount(const char *progname, const char *fsname,
|
||||
|
||||
@@ -1,444 +0,0 @@
|
||||
/*
|
||||
libulockmgr: Userspace Lock Manager Library
|
||||
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPLv2.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#include "ulockmgr.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct message {
|
||||
unsigned intr : 1;
|
||||
unsigned nofd : 1;
|
||||
pthread_t thr;
|
||||
int cmd;
|
||||
int fd;
|
||||
struct flock lock;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct fd_store {
|
||||
struct fd_store *next;
|
||||
int fd;
|
||||
int inuse;
|
||||
};
|
||||
|
||||
struct owner {
|
||||
struct owner *next;
|
||||
struct owner *prev;
|
||||
struct fd_store *fds;
|
||||
void *id;
|
||||
size_t id_len;
|
||||
int cfd;
|
||||
};
|
||||
|
||||
static pthread_mutex_t ulockmgr_lock;
|
||||
static int ulockmgr_cfd = -1;
|
||||
static struct owner owner_list = { .next = &owner_list, .prev = &owner_list };
|
||||
|
||||
#define MAX_SEND_FDS 2
|
||||
|
||||
static void list_del_owner(struct owner *owner)
|
||||
{
|
||||
struct owner *prev = owner->prev;
|
||||
struct owner *next = owner->next;
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
static void list_add_owner(struct owner *owner, struct owner *next)
|
||||
{
|
||||
struct owner *prev = next->prev;
|
||||
owner->next = next;
|
||||
owner->prev = prev;
|
||||
prev->next = owner;
|
||||
next->prev = owner;
|
||||
}
|
||||
|
||||
/*
|
||||
* There's a bug in the linux kernel (< 2.6.22) recv() implementation
|
||||
* on AF_UNIX, SOCK_STREAM sockets, that could cause it to return
|
||||
* zero, even if data was available. Retrying the recv will return
|
||||
* the data in this case.
|
||||
*/
|
||||
static int do_recv(int sock, void *buf, size_t len, int flags)
|
||||
{
|
||||
int res = recv(sock, buf, len, flags);
|
||||
if (res == 0)
|
||||
res = recv(sock, buf, len, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ulockmgr_send_message(int sock, void *buf, size_t buflen,
|
||||
int *fdp, int numfds)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *p_cmsg;
|
||||
struct iovec vec;
|
||||
size_t cmsgbuf[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
|
||||
int res;
|
||||
|
||||
assert(numfds <= MAX_SEND_FDS);
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
p_cmsg = CMSG_FIRSTHDR(&msg);
|
||||
p_cmsg->cmsg_level = SOL_SOCKET;
|
||||
p_cmsg->cmsg_type = SCM_RIGHTS;
|
||||
p_cmsg->cmsg_len = CMSG_LEN(sizeof(int) * numfds);
|
||||
memcpy(CMSG_DATA(p_cmsg), fdp, sizeof(int) * numfds);
|
||||
msg.msg_controllen = p_cmsg->cmsg_len;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
vec.iov_base = buf;
|
||||
vec.iov_len = buflen;
|
||||
res = sendmsg(sock, &msg, MSG_NOSIGNAL);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: sendmsg");
|
||||
return -1;
|
||||
}
|
||||
if ((size_t) res != buflen) {
|
||||
fprintf(stderr, "libulockmgr: sendmsg short\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulockmgr_start_daemon(void)
|
||||
{
|
||||
int sv[2];
|
||||
int res;
|
||||
char tmp[64];
|
||||
|
||||
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: socketpair");
|
||||
return -1;
|
||||
}
|
||||
snprintf(tmp, sizeof(tmp), "exec ulockmgr_server %i", sv[0]);
|
||||
res = system(tmp);
|
||||
close(sv[0]);
|
||||
if (res == -1 || !WIFEXITED(res) || WEXITSTATUS(res) != 0) {
|
||||
close(sv[1]);
|
||||
return -1;
|
||||
}
|
||||
ulockmgr_cfd = sv[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct owner *ulockmgr_new_owner(const void *id, size_t id_len)
|
||||
{
|
||||
int sv[2];
|
||||
int res;
|
||||
char c = 'm';
|
||||
struct owner *o;
|
||||
|
||||
if (ulockmgr_cfd == -1 && ulockmgr_start_daemon() == -1)
|
||||
return NULL;
|
||||
|
||||
o = calloc(1, sizeof(struct owner) + id_len);
|
||||
if (!o) {
|
||||
fprintf(stderr, "libulockmgr: failed to allocate memory\n");
|
||||
return NULL;
|
||||
}
|
||||
o->id = o + 1;
|
||||
o->id_len = id_len;
|
||||
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: socketpair");
|
||||
goto out_free;
|
||||
}
|
||||
res = ulockmgr_send_message(ulockmgr_cfd, &c, sizeof(c), &sv[0], 1);
|
||||
close(sv[0]);
|
||||
if (res == -1) {
|
||||
close(ulockmgr_cfd);
|
||||
ulockmgr_cfd = -1;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
o->cfd = sv[1];
|
||||
memcpy(o->id, id, id_len);
|
||||
list_add_owner(o, &owner_list);
|
||||
|
||||
return o;
|
||||
|
||||
out_close:
|
||||
close(sv[1]);
|
||||
out_free:
|
||||
free(o);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int ulockmgr_send_request(struct message *msg, const void *id,
|
||||
size_t id_len)
|
||||
{
|
||||
int sv[2];
|
||||
int cfd;
|
||||
struct owner *o;
|
||||
struct fd_store *f = NULL;
|
||||
struct fd_store *newf = NULL;
|
||||
struct fd_store **fp;
|
||||
int fd = msg->fd;
|
||||
int cmd = msg->cmd;
|
||||
int res;
|
||||
int unlockall = (cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
|
||||
msg->lock.l_start == 0 && msg->lock.l_len == 0);
|
||||
|
||||
for (o = owner_list.next; o != &owner_list; o = o->next)
|
||||
if (o->id_len == id_len && memcmp(o->id, id, id_len) == 0)
|
||||
break;
|
||||
|
||||
if (o == &owner_list)
|
||||
o = NULL;
|
||||
|
||||
if (!o && cmd != F_GETLK && msg->lock.l_type != F_UNLCK)
|
||||
o = ulockmgr_new_owner(id, id_len);
|
||||
|
||||
if (!o) {
|
||||
if (cmd == F_GETLK) {
|
||||
res = fcntl(msg->fd, F_GETLK, &msg->lock);
|
||||
return (res == -1) ? -errno : 0;
|
||||
} else if (msg->lock.l_type == F_UNLCK)
|
||||
return 0;
|
||||
else
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
if (unlockall)
|
||||
msg->nofd = 1;
|
||||
else {
|
||||
for (fp = &o->fds; *fp; fp = &(*fp)->next) {
|
||||
f = *fp;
|
||||
if (f->fd == fd) {
|
||||
msg->nofd = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!msg->nofd) {
|
||||
newf = f = calloc(1, sizeof(struct fd_store));
|
||||
if (!f) {
|
||||
fprintf(stderr, "libulockmgr: failed to allocate memory\n");
|
||||
return -ENOLCK;
|
||||
}
|
||||
}
|
||||
|
||||
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: socketpair");
|
||||
free(newf);
|
||||
return -ENOLCK;
|
||||
}
|
||||
|
||||
cfd = sv[1];
|
||||
sv[1] = msg->fd;
|
||||
res = ulockmgr_send_message(o->cfd, msg, sizeof(struct message), sv,
|
||||
msg->nofd ? 1 : 2);
|
||||
close(sv[0]);
|
||||
if (res == -1) {
|
||||
free(newf);
|
||||
close(cfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (newf) {
|
||||
newf->fd = msg->fd;
|
||||
newf->next = o->fds;
|
||||
o->fds = newf;
|
||||
}
|
||||
if (f)
|
||||
f->inuse++;
|
||||
|
||||
res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: recv");
|
||||
msg->error = EIO;
|
||||
} else if (res != sizeof(struct message)) {
|
||||
fprintf(stderr, "libulockmgr: recv short\n");
|
||||
msg->error = EIO;
|
||||
} else if (cmd == F_SETLKW && msg->error == EAGAIN) {
|
||||
pthread_mutex_unlock(&ulockmgr_lock);
|
||||
while (1) {
|
||||
sigset_t old;
|
||||
sigset_t unblock;
|
||||
int errno_save;
|
||||
|
||||
sigemptyset(&unblock);
|
||||
sigaddset(&unblock, SIGUSR1);
|
||||
pthread_sigmask(SIG_UNBLOCK, &unblock, &old);
|
||||
res = do_recv(cfd, msg, sizeof(struct message),
|
||||
MSG_WAITALL);
|
||||
errno_save = errno;
|
||||
pthread_sigmask(SIG_SETMASK, &old, NULL);
|
||||
if (res == sizeof(struct message))
|
||||
break;
|
||||
else if (res >= 0) {
|
||||
fprintf(stderr, "libulockmgr: recv short\n");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
} else if (errno_save != EINTR) {
|
||||
errno = errno_save;
|
||||
perror("libulockmgr: recv");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
}
|
||||
msg->intr = 1;
|
||||
res = send(o->cfd, msg, sizeof(struct message),
|
||||
MSG_NOSIGNAL);
|
||||
if (res == -1) {
|
||||
perror("libulockmgr: send");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
}
|
||||
if (res != sizeof(struct message)) {
|
||||
fprintf(stderr, "libulockmgr: send short\n");
|
||||
msg->error = EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&ulockmgr_lock);
|
||||
|
||||
}
|
||||
if (f)
|
||||
f->inuse--;
|
||||
close(cfd);
|
||||
if (unlockall) {
|
||||
for (fp = &o->fds; *fp;) {
|
||||
f = *fp;
|
||||
if (f->fd == fd && !f->inuse) {
|
||||
*fp = f->next;
|
||||
free(f);
|
||||
} else
|
||||
fp = &f->next;
|
||||
}
|
||||
if (!o->fds) {
|
||||
list_del_owner(o);
|
||||
close(o->cfd);
|
||||
free(o);
|
||||
}
|
||||
/* Force OK on unlock-all, since it _will_ succeed once the
|
||||
owner is deleted */
|
||||
msg->error = 0;
|
||||
}
|
||||
|
||||
return -msg->error;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static uint32_t owner_hash(const unsigned char *id, size_t id_len)
|
||||
{
|
||||
uint32_t h = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < id_len; i++)
|
||||
h = ((h << 8) | (h >> 24)) ^ id[i];
|
||||
|
||||
return h;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ulockmgr_canonicalize(int fd, struct flock *lock)
|
||||
{
|
||||
off_t offset;
|
||||
if (lock->l_whence == SEEK_CUR) {
|
||||
offset = lseek(fd, 0, SEEK_CUR);
|
||||
if (offset == (off_t) -1)
|
||||
return -errno;
|
||||
} else if (lock->l_whence == SEEK_END) {
|
||||
struct stat stbuf;
|
||||
int res = fstat(fd, &stbuf);
|
||||
if (res == -1)
|
||||
return -errno;
|
||||
|
||||
offset = stbuf.st_size;
|
||||
} else
|
||||
offset = 0;
|
||||
|
||||
lock->l_whence = SEEK_SET;
|
||||
lock->l_start += offset;
|
||||
|
||||
if (lock->l_start < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (lock->l_len < 0) {
|
||||
lock->l_start += lock->l_len;
|
||||
if (lock->l_start < 0)
|
||||
return -EINVAL;
|
||||
lock->l_len = -lock->l_len;
|
||||
}
|
||||
if (lock->l_len && lock->l_start + lock->l_len - 1 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ulockmgr_op(int fd, int cmd, struct flock *lock, const void *owner,
|
||||
size_t owner_len)
|
||||
{
|
||||
int err;
|
||||
struct message msg;
|
||||
sigset_t old;
|
||||
sigset_t block;
|
||||
|
||||
if (cmd != F_GETLK && cmd != F_SETLK && cmd != F_SETLKW)
|
||||
return -EINVAL;
|
||||
|
||||
if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK &&
|
||||
lock->l_type != F_UNLCK)
|
||||
return -EINVAL;
|
||||
|
||||
if (lock->l_whence != SEEK_SET && lock->l_whence != SEEK_CUR &&
|
||||
lock->l_whence != SEEK_END)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "libulockmgr: %i %i %i %lli %lli own: 0x%08x\n",
|
||||
cmd, lock->l_type, lock->l_whence, lock->l_start, lock->l_len,
|
||||
owner_hash(owner, owner_len));
|
||||
#endif
|
||||
|
||||
/* Unlock should never block anyway */
|
||||
if (cmd == F_SETLKW && lock->l_type == F_UNLCK)
|
||||
cmd = F_SETLK;
|
||||
|
||||
memset(&msg, 0, sizeof(struct message));
|
||||
msg.cmd = cmd;
|
||||
msg.fd = fd;
|
||||
msg.lock = *lock;
|
||||
err = ulockmgr_canonicalize(fd, &msg.lock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sigemptyset(&block);
|
||||
sigaddset(&block, SIGUSR1);
|
||||
pthread_sigmask(SIG_BLOCK, &block, &old);
|
||||
pthread_mutex_lock(&ulockmgr_lock);
|
||||
err = ulockmgr_send_request(&msg, owner, owner_len);
|
||||
pthread_mutex_unlock(&ulockmgr_lock);
|
||||
pthread_sigmask(SIG_SETMASK, &old, NULL);
|
||||
if (!err && cmd == F_GETLK) {
|
||||
if (msg.lock.l_type == F_UNLCK)
|
||||
lock->l_type = F_UNLCK;
|
||||
else
|
||||
*lock = msg.lock;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
AM_CPPFLAGS = -D_FILE_OFFSET_BITS=64
|
||||
bin_PROGRAMS = fusermount ulockmgr_server
|
||||
noinst_PROGRAMS = mount.fuse
|
||||
|
||||
# we re-use mount_util.c from the library, but do want to keep ourself
|
||||
# as stand-alone as possible. in order to make an out-of-source build
|
||||
# possible, we "generate" the file from its original location by
|
||||
# copying it over.
|
||||
fusermount_SOURCES = fusermount.c mount_util.c
|
||||
fusermount_CPPFLAGS = -I$(top_srcdir)/lib
|
||||
BUILT_SOURCES = mount_util.c
|
||||
mount_util.c: $(top_srcdir)/lib/mount_util.c
|
||||
@cp $(top_srcdir)/lib/mount_util.c .
|
||||
|
||||
mount_fuse_SOURCES = mount.fuse.c
|
||||
|
||||
ulockmgr_server_SOURCES = ulockmgr_server.c
|
||||
ulockmgr_server_CPPFLAGS = -D_FILE_OFFSET_BITS=64 -D_REENTRANT
|
||||
ulockmgr_server_LDFLAGS = -pthread
|
||||
|
||||
install-exec-hook:
|
||||
-chmod u+s $(DESTDIR)$(bindir)/fusermount
|
||||
@if test ! -e $(DESTDIR)/dev/fuse; then \
|
||||
$(MKDIR_P) $(DESTDIR)/dev; \
|
||||
echo "mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true"; \
|
||||
mknod $(DESTDIR)/dev/fuse -m 0666 c 10 229 || true; \
|
||||
fi
|
||||
|
||||
EXTRA_DIST = udev.rules init_script
|
||||
|
||||
MOUNT_FUSE_PATH = @MOUNT_FUSE_PATH@
|
||||
UDEV_RULES_PATH = @UDEV_RULES_PATH@
|
||||
INIT_D_PATH = @INIT_D_PATH@
|
||||
|
||||
install-exec-local:
|
||||
$(MKDIR_P) $(DESTDIR)$(MOUNT_FUSE_PATH)
|
||||
$(INSTALL_PROGRAM) $(builddir)/mount.fuse $(DESTDIR)$(MOUNT_FUSE_PATH)/mount.fuse
|
||||
$(MKDIR_P) $(DESTDIR)$(INIT_D_PATH)
|
||||
$(INSTALL_SCRIPT) $(srcdir)/init_script $(DESTDIR)$(INIT_D_PATH)/fuse
|
||||
@if test -x /usr/sbin/update-rc.d; then \
|
||||
echo "/usr/sbin/update-rc.d fuse start 34 S . start 41 0 6 . || true"; \
|
||||
/usr/sbin/update-rc.d fuse start 34 S . start 41 0 6 . || true; \
|
||||
fi
|
||||
|
||||
install-data-local:
|
||||
$(MKDIR_P) $(DESTDIR)$(UDEV_RULES_PATH)
|
||||
$(INSTALL_DATA) $(srcdir)/udev.rules $(DESTDIR)$(UDEV_RULES_PATH)/99-fuse.rules
|
||||
|
||||
uninstall-local:
|
||||
rm -f $(DESTDIR)$(MOUNT_FUSE_PATH)/mount.fuse
|
||||
rm -f $(DESTDIR)$(UDEV_RULES_PATH)/99-fuse.rules
|
||||
rm -f $(DESTDIR)$(INIT_D_PATH)/fuse
|
||||
@if test -x /usr/sbin/update-rc.d; then \
|
||||
echo "/usr/sbin/update-rc.d fuse remove || true"; \
|
||||
/usr/sbin/update-rc.d fuse remove || true; \
|
||||
fi
|
||||
@@ -1293,7 +1293,7 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
|
||||
/* Become a daemon and wait for the parent to exit or die.
|
||||
ie For the control socket to get closed.
|
||||
ie For the control socket to get closed.
|
||||
btw We don't want to use daemon() function here because
|
||||
it forks and messes with the file descriptors. */
|
||||
setsid();
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
#! /bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: fuse
|
||||
# Required-Start:
|
||||
# Should-Start: udev
|
||||
# Required-Stop:
|
||||
# Default-Start: S
|
||||
# Default-Stop:
|
||||
# Short-Description: Start and stop fuse.
|
||||
# Description: Load the fuse module and mount the fuse control
|
||||
# filesystem.
|
||||
### END INIT INFO
|
||||
|
||||
set -e
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||
MOUNTPOINT=/sys/fs/fuse/connections
|
||||
|
||||
# Gracefully exit if the package has been removed.
|
||||
which fusermount &>/dev/null || exit 5
|
||||
|
||||
case "$1" in
|
||||
start|restart|force-reload)
|
||||
if ! grep -qw fuse /proc/filesystems; then
|
||||
echo -n "Loading fuse module"
|
||||
if ! modprobe fuse >/dev/null 2>&1; then
|
||||
echo " failed!"
|
||||
exit 1
|
||||
else
|
||||
echo "."
|
||||
fi
|
||||
else
|
||||
echo "Fuse filesystem already available."
|
||||
fi
|
||||
if grep -qw fusectl /proc/filesystems && \
|
||||
! grep -qw $MOUNTPOINT /proc/mounts; then
|
||||
echo -n "Mounting fuse control filesystem"
|
||||
if ! mount -t fusectl fusectl $MOUNTPOINT >/dev/null 2>&1; then
|
||||
echo " failed!"
|
||||
exit 1
|
||||
else
|
||||
echo "."
|
||||
fi
|
||||
else
|
||||
echo "Fuse control filesystem already available."
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
if ! grep -qw fuse /proc/filesystems; then
|
||||
echo "Fuse filesystem not loaded."
|
||||
exit 7
|
||||
fi
|
||||
if grep -qw $MOUNTPOINT /proc/mounts; then
|
||||
echo -n "Unmounting fuse control filesystem"
|
||||
if ! umount $MOUNTPOINT >/dev/null 2>&1; then
|
||||
echo " failed!"
|
||||
else
|
||||
echo "."
|
||||
fi
|
||||
else
|
||||
echo "Fuse control filesystem not mounted."
|
||||
fi
|
||||
if grep -qw "^fuse" /proc/modules; then
|
||||
echo -n "Unloading fuse module"
|
||||
if ! rmmod fuse >/dev/null 2>&1; then
|
||||
echo " failed!"
|
||||
else
|
||||
echo "."
|
||||
fi
|
||||
else
|
||||
echo "Fuse module not loaded."
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
echo -n "Checking fuse filesystem"
|
||||
if ! grep -qw fuse /proc/filesystems; then
|
||||
echo " not available."
|
||||
exit 3
|
||||
else
|
||||
echo " ok."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|force-reload|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -93,6 +93,7 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
basename = argv[0];
|
||||
|
||||
type = "mergerfs";
|
||||
if (strncmp(basename, "mount.fuse.", 11) == 0)
|
||||
type = basename + 11;
|
||||
if (strncmp(basename, "mount.fuseblk.", 14) == 0)
|
||||
@@ -1 +0,0 @@
|
||||
KERNEL=="fuse", MODE="0666"
|
||||
@@ -1,425 +0,0 @@
|
||||
/*
|
||||
ulockmgr_server: Userspace Lock Manager Server
|
||||
Copyright (C) 2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
struct message {
|
||||
unsigned intr : 1;
|
||||
unsigned nofd : 1;
|
||||
pthread_t thr;
|
||||
int cmd;
|
||||
int fd;
|
||||
struct flock lock;
|
||||
int error;
|
||||
};
|
||||
|
||||
struct fd_store {
|
||||
struct fd_store *next;
|
||||
int fd;
|
||||
int origfd;
|
||||
int inuse;
|
||||
};
|
||||
|
||||
struct owner {
|
||||
struct fd_store *fds;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
struct req_data {
|
||||
struct owner *o;
|
||||
int cfd;
|
||||
struct fd_store *f;
|
||||
struct message msg;
|
||||
};
|
||||
|
||||
#define MAX_SEND_FDS 2
|
||||
|
||||
static int receive_message(int sock, void *buf, size_t buflen, int *fdp,
|
||||
int *numfds)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
size_t ccmsg[CMSG_SPACE(sizeof(int) * MAX_SEND_FDS) / sizeof(size_t)];
|
||||
struct cmsghdr *cmsg;
|
||||
int res;
|
||||
int i;
|
||||
|
||||
assert(*numfds <= MAX_SEND_FDS);
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = buflen;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(ccmsg, -1, sizeof(ccmsg));
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = ccmsg;
|
||||
msg.msg_controllen = sizeof(ccmsg);
|
||||
|
||||
res = recvmsg(sock, &msg, MSG_WAITALL);
|
||||
if (!res) {
|
||||
/* retry on zero return, see do_recv() in ulockmgr.c */
|
||||
res = recvmsg(sock, &msg, MSG_WAITALL);
|
||||
if (!res)
|
||||
return 0;
|
||||
}
|
||||
if (res == -1) {
|
||||
perror("ulockmgr_server: recvmsg");
|
||||
return -1;
|
||||
}
|
||||
if ((size_t) res != buflen) {
|
||||
fprintf(stderr, "ulockmgr_server: short message received\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg) {
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: unknown control message %d\n",
|
||||
cmsg->cmsg_type);
|
||||
return -1;
|
||||
}
|
||||
memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
|
||||
if (msg.msg_flags & MSG_CTRUNC) {
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: control message truncated\n");
|
||||
for (i = 0; i < *numfds; i++)
|
||||
close(fdp[i]);
|
||||
*numfds = 0;
|
||||
}
|
||||
} else {
|
||||
if (msg.msg_flags & MSG_CTRUNC) {
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: control message truncated(*)\n");
|
||||
|
||||
/* There's a bug in the Linux kernel, that if
|
||||
not all file descriptors were allocated,
|
||||
then the cmsg header is not filled in */
|
||||
cmsg = (struct cmsghdr *) ccmsg;
|
||||
memcpy(fdp, CMSG_DATA(cmsg), sizeof(int) * *numfds);
|
||||
for (i = 0; i < *numfds; i++)
|
||||
close(fdp[i]);
|
||||
}
|
||||
*numfds = 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int closefrom(int minfd)
|
||||
{
|
||||
DIR *dir = opendir("/proc/self/fd");
|
||||
if (dir) {
|
||||
int dfd = dirfd(dir);
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(dir))) {
|
||||
char *end;
|
||||
int fd = strtol(ent->d_name, &end, 10);
|
||||
if (ent->d_name[0] && !end[0] && fd >= minfd &&
|
||||
fd != dfd)
|
||||
close(fd);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_reply(int cfd, struct message *msg)
|
||||
{
|
||||
int res = send(cfd, msg, sizeof(struct message), MSG_NOSIGNAL);
|
||||
if (res == -1)
|
||||
perror("ulockmgr_server: sending reply");
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "ulockmgr_server: error: %i\n", msg->error);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *process_request(void *d_)
|
||||
{
|
||||
struct req_data *d = d_;
|
||||
int res;
|
||||
|
||||
assert(d->msg.cmd == F_SETLKW);
|
||||
res = fcntl(d->f->fd, F_SETLK, &d->msg.lock);
|
||||
if (res == -1 && errno == EAGAIN) {
|
||||
d->msg.error = EAGAIN;
|
||||
d->msg.thr = pthread_self();
|
||||
send_reply(d->cfd, &d->msg);
|
||||
res = fcntl(d->f->fd, F_SETLKW, &d->msg.lock);
|
||||
}
|
||||
d->msg.error = (res == -1) ? errno : 0;
|
||||
pthread_mutex_lock(&d->o->lock);
|
||||
d->f->inuse--;
|
||||
pthread_mutex_unlock(&d->o->lock);
|
||||
send_reply(d->cfd, &d->msg);
|
||||
close(d->cfd);
|
||||
free(d);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void process_message(struct owner *o, struct message *msg, int cfd,
|
||||
int fd)
|
||||
{
|
||||
struct fd_store *f = NULL;
|
||||
struct fd_store *newf = NULL;
|
||||
struct fd_store **fp;
|
||||
struct req_data *d;
|
||||
pthread_t tid;
|
||||
int res;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "ulockmgr_server: %i %i %i %lli %lli\n",
|
||||
msg->cmd, msg->lock.l_type, msg->lock.l_whence,
|
||||
msg->lock.l_start, msg->lock.l_len);
|
||||
#endif
|
||||
|
||||
if (msg->cmd == F_SETLK && msg->lock.l_type == F_UNLCK &&
|
||||
msg->lock.l_start == 0 && msg->lock.l_len == 0) {
|
||||
for (fp = &o->fds; *fp;) {
|
||||
f = *fp;
|
||||
if (f->origfd == msg->fd && !f->inuse) {
|
||||
close(f->fd);
|
||||
*fp = f->next;
|
||||
free(f);
|
||||
} else
|
||||
fp = &f->next;
|
||||
}
|
||||
if (!msg->nofd)
|
||||
close(fd);
|
||||
|
||||
msg->error = 0;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->nofd) {
|
||||
for (fp = &o->fds; *fp; fp = &(*fp)->next) {
|
||||
f = *fp;
|
||||
if (f->origfd == msg->fd)
|
||||
break;
|
||||
}
|
||||
if (!*fp) {
|
||||
fprintf(stderr, "ulockmgr_server: fd %i not found\n",
|
||||
msg->fd);
|
||||
msg->error = EIO;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
newf = f = malloc(sizeof(struct fd_store));
|
||||
if (!f) {
|
||||
msg->error = ENOLCK;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
return;
|
||||
}
|
||||
|
||||
f->fd = fd;
|
||||
f->origfd = msg->fd;
|
||||
f->inuse = 0;
|
||||
}
|
||||
|
||||
if (msg->cmd == F_GETLK || msg->cmd == F_SETLK ||
|
||||
msg->lock.l_type == F_UNLCK) {
|
||||
res = fcntl(f->fd, msg->cmd, &msg->lock);
|
||||
msg->error = (res == -1) ? errno : 0;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
if (newf) {
|
||||
newf->next = o->fds;
|
||||
o->fds = newf;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
d = malloc(sizeof(struct req_data));
|
||||
if (!d) {
|
||||
msg->error = ENOLCK;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
free(newf);
|
||||
return;
|
||||
}
|
||||
|
||||
f->inuse++;
|
||||
d->o = o;
|
||||
d->cfd = cfd;
|
||||
d->f = f;
|
||||
d->msg = *msg;
|
||||
res = pthread_create(&tid, NULL, process_request, d);
|
||||
if (res) {
|
||||
msg->error = ENOLCK;
|
||||
send_reply(cfd, msg);
|
||||
close(cfd);
|
||||
free(d);
|
||||
f->inuse--;
|
||||
free(newf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newf) {
|
||||
newf->next = o->fds;
|
||||
o->fds = newf;
|
||||
}
|
||||
pthread_detach(tid);
|
||||
}
|
||||
|
||||
static void sigusr1_handler(int sig)
|
||||
{
|
||||
(void) sig;
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
static void process_owner(int cfd)
|
||||
{
|
||||
struct owner o;
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(struct sigaction));
|
||||
sa.sa_handler = sigusr1_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
|
||||
perror("ulockmgr_server: cannot set sigusr1 signal handler");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&o, 0, sizeof(struct owner));
|
||||
pthread_mutex_init(&o.lock, NULL);
|
||||
while (1) {
|
||||
struct message msg;
|
||||
int rfds[2];
|
||||
int res;
|
||||
int numfds = 2;
|
||||
|
||||
res = receive_message(cfd, &msg, sizeof(msg), rfds, &numfds);
|
||||
if (!res)
|
||||
break;
|
||||
if (res == -1)
|
||||
exit(1);
|
||||
|
||||
if (msg.intr) {
|
||||
if (numfds != 0)
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: too many fds for intr\n");
|
||||
pthread_kill(msg.thr, SIGUSR1);
|
||||
} else {
|
||||
if (numfds != 2)
|
||||
continue;
|
||||
|
||||
pthread_mutex_lock(&o.lock);
|
||||
process_message(&o, &msg, rfds[0], rfds[1]);
|
||||
pthread_mutex_unlock(&o.lock);
|
||||
}
|
||||
}
|
||||
if (o.fds)
|
||||
fprintf(stderr,
|
||||
"ulockmgr_server: open file descriptors on exit\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int nullfd;
|
||||
char *end;
|
||||
int cfd;
|
||||
sigset_t empty;
|
||||
|
||||
if (argc != 2 || !argv[1][0])
|
||||
goto out_inval;
|
||||
|
||||
cfd = strtol(argv[1], &end, 10);
|
||||
if (*end)
|
||||
goto out_inval;
|
||||
|
||||
/* demonize current process */
|
||||
switch(fork()) {
|
||||
case -1:
|
||||
perror("ulockmgr_server: fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (setsid() == -1) {
|
||||
perror("ulockmgr_server: setsid");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
(void) chdir("/");
|
||||
|
||||
sigemptyset(&empty);
|
||||
sigprocmask(SIG_SETMASK, &empty, NULL);
|
||||
|
||||
if (dup2(cfd, 4) == -1) {
|
||||
perror("ulockmgr_server: dup2");
|
||||
exit(1);
|
||||
}
|
||||
cfd = 4;
|
||||
nullfd = open("/dev/null", O_RDWR);
|
||||
if (nullfd >= 0) {
|
||||
dup2(nullfd, 0);
|
||||
dup2(nullfd, 1);
|
||||
}
|
||||
close(3);
|
||||
closefrom(5);
|
||||
while (1) {
|
||||
char c;
|
||||
int sock;
|
||||
int pid;
|
||||
int numfds = 1;
|
||||
int res = receive_message(cfd, &c, sizeof(c), &sock, &numfds);
|
||||
if (!res)
|
||||
break;
|
||||
if (res == -1)
|
||||
exit(1);
|
||||
assert(numfds == 1);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("ulockmgr_server: fork");
|
||||
close(sock);
|
||||
continue;
|
||||
}
|
||||
if (pid == 0) {
|
||||
close(cfd);
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("ulockmgr_server: fork");
|
||||
_exit(1);
|
||||
}
|
||||
if (pid == 0)
|
||||
process_owner(sock);
|
||||
_exit(0);
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
close(sock);
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_inval:
|
||||
fprintf(stderr, "%s should be started by libulockmgr\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
+886
-358
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -1,7 +1,7 @@
|
||||
Name: mergerfs
|
||||
Version: __VERSION__
|
||||
Release: 1%{?dist}
|
||||
Summary: A FUSE union filesystem
|
||||
Summary: A featureful FUSE based union filesystem
|
||||
|
||||
Group: Applications/System
|
||||
License: ISC
|
||||
@@ -32,7 +32,9 @@ make %{?_smp_mflags}
|
||||
make install PREFIX=%{_prefix} DESTDIR=%{buildroot}
|
||||
|
||||
%files
|
||||
%{_bindir}/*
|
||||
/usr/bin/mergerfs
|
||||
/usr/bin/mergerfs-fusermount
|
||||
/sbin/mount.mergerfs
|
||||
%doc %{_mandir}/*
|
||||
|
||||
%changelog
|
||||
|
||||
+388
-149
@@ -17,65 +17,396 @@
|
||||
*/
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs_glob.hpp"
|
||||
#include "fs_realpathize.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "num.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using nonstd::optional;
|
||||
|
||||
|
||||
Branch::Branch(const uint64_t &default_minfreespace_)
|
||||
: _default_minfreespace(&default_minfreespace_)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
Branch::from_string(const std::string &str_)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
std::string
|
||||
Branch::to_string(void) const
|
||||
{
|
||||
std::string rv;
|
||||
|
||||
rv = path;
|
||||
rv += '=';
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case Branch::Mode::RW:
|
||||
rv += "RW";
|
||||
break;
|
||||
case Branch::Mode::RO:
|
||||
rv += "RO";
|
||||
break;
|
||||
case Branch::Mode::NC:
|
||||
rv += "NC";
|
||||
break;
|
||||
}
|
||||
|
||||
if(_minfreespace.has_value())
|
||||
{
|
||||
rv += ',';
|
||||
rv += num::humanize(_minfreespace.value());
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
Branch::set_minfreespace(const uint64_t minfreespace_)
|
||||
{
|
||||
_minfreespace = minfreespace_;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
Branch::minfreespace(void) const
|
||||
{
|
||||
if(_minfreespace.has_value())
|
||||
return _minfreespace.value();
|
||||
return *_default_minfreespace;
|
||||
}
|
||||
|
||||
bool
|
||||
Branch::ro(void) const
|
||||
{
|
||||
return (mode == Branch::RO);
|
||||
return (mode == Branch::Mode::RO);
|
||||
}
|
||||
|
||||
bool
|
||||
Branch::nc(void) const
|
||||
{
|
||||
return (mode == Branch::NC);
|
||||
return (mode == Branch::Mode::NC);
|
||||
}
|
||||
|
||||
bool
|
||||
Branch::ro_or_nc(void) const
|
||||
{
|
||||
return ((mode == Branch::RO) ||
|
||||
(mode == Branch::NC));
|
||||
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(const bool mode_) const
|
||||
Branches::to_string(void) const
|
||||
{
|
||||
rwlock::ReadGuard guard(lock);
|
||||
|
||||
string tmp;
|
||||
|
||||
for(size_t i = 0; i < size(); i++)
|
||||
for(size_t i = 0; i < vec.size(); i++)
|
||||
{
|
||||
const Branch &branch = (*this)[i];
|
||||
|
||||
tmp += branch.path;
|
||||
|
||||
if(mode_)
|
||||
{
|
||||
tmp += '=';
|
||||
switch(branch.mode)
|
||||
{
|
||||
default:
|
||||
case Branch::RW:
|
||||
tmp += "RW";
|
||||
break;
|
||||
case Branch::RO:
|
||||
tmp += "RO";
|
||||
break;
|
||||
case Branch::NC:
|
||||
tmp += "NC";
|
||||
break;
|
||||
}
|
||||
}
|
||||
const Branch &branch = vec[i];
|
||||
|
||||
tmp += branch.to_string();
|
||||
tmp += ':';
|
||||
}
|
||||
|
||||
@@ -88,135 +419,43 @@ Branches::to_string(const bool mode_) const
|
||||
void
|
||||
Branches::to_paths(vector<string> &vec_) const
|
||||
{
|
||||
for(size_t i = 0; i < size(); i++)
|
||||
rwlock::ReadGuard guard(lock);
|
||||
|
||||
for(size_t i = 0; i < vec.size(); i++)
|
||||
{
|
||||
const Branch &branch = (*this)[i];
|
||||
const Branch &branch = vec[i];
|
||||
|
||||
vec_.push_back(branch.path);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse(const string &str_,
|
||||
Branches &branches_)
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
: _branches(b_)
|
||||
{
|
||||
string str;
|
||||
Branch branch;
|
||||
vector<string> globbed;
|
||||
|
||||
str = str_;
|
||||
branch.mode = Branch::INVALID;
|
||||
if(str::ends_with(str,"=RO"))
|
||||
branch.mode = Branch::RO;
|
||||
else if(str::ends_with(str,"=RW"))
|
||||
branch.mode = Branch::RW;
|
||||
else if(str::ends_with(str,"=NC"))
|
||||
branch.mode = Branch::NC;
|
||||
}
|
||||
|
||||
if(branch.mode != Branch::INVALID)
|
||||
str.resize(str.size() - 3);
|
||||
else
|
||||
branch.mode = Branch::RW;
|
||||
int
|
||||
SrcMounts::from_string(const std::string &s_)
|
||||
{
|
||||
return _branches.from_string(s_);
|
||||
}
|
||||
|
||||
fs::glob(str,globbed);
|
||||
fs::realpathize(globbed);
|
||||
for(size_t i = 0; i < globbed.size(); i++)
|
||||
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++)
|
||||
{
|
||||
branch.path = globbed[i];
|
||||
branches_.push_back(branch);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Branches::set(const std::string &str_)
|
||||
{
|
||||
vector<string> paths;
|
||||
|
||||
clear();
|
||||
|
||||
str::split(paths,str_,':');
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
Branches branches;
|
||||
|
||||
parse(paths[i],branches);
|
||||
|
||||
insert(end(),
|
||||
branches.begin(),
|
||||
branches.end());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Branches::add_begin(const std::string &str_)
|
||||
{
|
||||
vector<string> paths;
|
||||
|
||||
str::split(paths,str_,':');
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
Branches branches;
|
||||
|
||||
parse(paths[i],branches);
|
||||
|
||||
insert(begin(),
|
||||
branches.begin(),
|
||||
branches.end());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Branches::add_end(const std::string &str_)
|
||||
{
|
||||
vector<string> paths;
|
||||
|
||||
str::split(paths,str_,':');
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
Branches branches;
|
||||
|
||||
parse(paths[i],branches);
|
||||
|
||||
insert(end(),
|
||||
branches.begin(),
|
||||
branches.end());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Branches::erase_begin(void)
|
||||
{
|
||||
erase(begin());
|
||||
}
|
||||
|
||||
void
|
||||
Branches::erase_end(void)
|
||||
{
|
||||
pop_back();
|
||||
}
|
||||
|
||||
void
|
||||
Branches::erase_fnmatch(const std::string &str_)
|
||||
{
|
||||
vector<string> patterns;
|
||||
|
||||
str::split(patterns,str_,':');
|
||||
|
||||
for(iterator i = begin(); i != 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) ? erase(i) : (i+1));
|
||||
rv += _branches.vec[i].path;
|
||||
rv += ':';
|
||||
}
|
||||
|
||||
if(*rv.rbegin() == ':')
|
||||
rv.erase(rv.size() - 1);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
+54
-12
@@ -18,12 +18,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rwlock.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Branch
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
|
||||
class Branch : public ToFromString
|
||||
{
|
||||
enum Mode
|
||||
public:
|
||||
Branch(const uint64_t &default_minfreespace);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str);
|
||||
std::string to_string(void) const;
|
||||
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
INVALID,
|
||||
RO,
|
||||
@@ -31,26 +46,53 @@ struct Branch
|
||||
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;
|
||||
|
||||
private:
|
||||
nonstd::optional<uint64_t> _minfreespace;
|
||||
const uint64_t *_default_minfreespace;
|
||||
};
|
||||
|
||||
class Branches : public std::vector<Branch>
|
||||
typedef std::vector<Branch> BranchVec;
|
||||
|
||||
class Branches : public ToFromString
|
||||
{
|
||||
public:
|
||||
std::string to_string(const bool mode_ = false) const;
|
||||
|
||||
void to_paths(std::vector<std::string> &vec_) const;
|
||||
Branches(const uint64_t &default_minfreespace_);
|
||||
|
||||
public:
|
||||
void set(const std::string &str_);
|
||||
void add_begin(const std::string &str_);
|
||||
void add_end(const std::string &str_);
|
||||
void erase_begin(void);
|
||||
void erase_end(void);
|
||||
void erase_fnmatch(const std::string &str_);
|
||||
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;
|
||||
};
|
||||
|
||||
+7
-6
@@ -17,21 +17,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
|
||||
template<typename K,typename V>
|
||||
class buildmap
|
||||
{
|
||||
public:
|
||||
buildmap(const K &key,
|
||||
const V &val)
|
||||
buildmap(const K &key_,
|
||||
const V &val_)
|
||||
{
|
||||
_map.insert(std::make_pair(key,val));
|
||||
_map.insert(std::make_pair(key_,val_));
|
||||
}
|
||||
|
||||
buildmap<K,V> &operator()(const K &key,
|
||||
const V &val)
|
||||
buildmap<K,V> &operator()(const K &key_,
|
||||
const V &val_)
|
||||
{
|
||||
_map.insert(std::make_pair(key,val));
|
||||
_map.insert(std::make_pair(key_,val_));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -23,14 +23,14 @@ template<typename V, bool SORT = false>
|
||||
class buildvector
|
||||
{
|
||||
public:
|
||||
buildvector(const V &val)
|
||||
buildvector(const V &val_)
|
||||
{
|
||||
_vector.push_back(val);
|
||||
_vector.push_back(val_);
|
||||
}
|
||||
|
||||
buildvector<V,SORT> &operator()(const V &val)
|
||||
buildvector<V,SORT> &operator()(const V &val_)
|
||||
{
|
||||
_vector.push_back(val);
|
||||
_vector.push_back(val_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "category.hpp"
|
||||
#include "buildvector.hpp"
|
||||
|
||||
#define CATEGORY(X) Category(Category::Enum::X,#X)
|
||||
|
||||
const std::vector<Category> Category::_categories_ =
|
||||
buildvector<Category,true>
|
||||
(CATEGORY(invalid))
|
||||
(CATEGORY(action))
|
||||
(CATEGORY(create))
|
||||
(CATEGORY(search));
|
||||
|
||||
const Category * const Category::categories = &_categories_[1];
|
||||
|
||||
const Category &Category::invalid = Category::categories[Category::Enum::invalid];
|
||||
const Category &Category::action = Category::categories[Category::Enum::action];
|
||||
const Category &Category::create = Category::categories[Category::Enum::create];
|
||||
const Category &Category::search = Category::categories[Category::Enum::search];
|
||||
|
||||
const Category&
|
||||
Category::find(const std::string &str)
|
||||
{
|
||||
for(int i = Enum::BEGIN; i != Enum::END; ++i)
|
||||
{
|
||||
if(categories[i] == str)
|
||||
return categories[i];
|
||||
}
|
||||
|
||||
return invalid;
|
||||
}
|
||||
|
||||
const Category&
|
||||
Category::find(const Category::Enum::Type i)
|
||||
{
|
||||
if(i >= Category::Enum::BEGIN &&
|
||||
i < Category::Enum::END)
|
||||
return categories[i];
|
||||
|
||||
return invalid;
|
||||
}
|
||||
+4
-64
@@ -16,69 +16,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Category
|
||||
{
|
||||
public:
|
||||
struct Enum
|
||||
enum class Category
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
invalid = -1,
|
||||
BEGIN = 0,
|
||||
action = BEGIN,
|
||||
create,
|
||||
search,
|
||||
END
|
||||
};
|
||||
ACTION,
|
||||
CREATE,
|
||||
SEARCH
|
||||
};
|
||||
|
||||
private:
|
||||
Enum::Type _enum;
|
||||
std::string _str;
|
||||
|
||||
public:
|
||||
Category()
|
||||
: _enum(invalid),
|
||||
_str(invalid)
|
||||
{
|
||||
}
|
||||
|
||||
Category(const Enum::Type enum_,
|
||||
const std::string &str_)
|
||||
: _enum(enum_),
|
||||
_str(str_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
operator const Enum::Type() const { return _enum; }
|
||||
operator const std::string&() const { return _str; }
|
||||
operator const Category*() const { return this; }
|
||||
|
||||
bool operator==(const std::string &str_) const
|
||||
{ return _str == str_; }
|
||||
|
||||
bool operator==(const Enum::Type enum_) const
|
||||
{ return _enum == enum_; }
|
||||
|
||||
bool operator!=(const Category &r) const
|
||||
{ return _enum != r._enum; }
|
||||
|
||||
bool operator<(const Category &r) const
|
||||
{ return _enum < r._enum; }
|
||||
|
||||
public:
|
||||
static const Category &find(const std::string&);
|
||||
static const Category &find(const Enum::Type);
|
||||
|
||||
public:
|
||||
static const std::vector<Category> _categories_;
|
||||
static const Category * const categories;
|
||||
static const Category &invalid;
|
||||
static const Category &action;
|
||||
static const Category &create;
|
||||
static const Category &search;
|
||||
};
|
||||
|
||||
+229
-151
@@ -15,185 +15,263 @@
|
||||
*/
|
||||
|
||||
#include "config.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "num.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "to_string.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MINFREESPACE_DEFAULT (4294967295ULL)
|
||||
#define POLICYINIT(X) X(policies[FuseFunc::Enum::X])
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
#define IFERT(S) if(S == s_) return true
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
bool
|
||||
readonly(const std::string &s_)
|
||||
{
|
||||
IFERT("async_read");
|
||||
IFERT("cache.symlinks");
|
||||
IFERT("cache.writeback");
|
||||
IFERT("fsname");
|
||||
IFERT("fuse_msg_size");
|
||||
IFERT("mount");
|
||||
IFERT("nullrw");
|
||||
IFERT("pid");
|
||||
IFERT("readdirplus");
|
||||
IFERT("threads");
|
||||
IFERT("version");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
: destmount(),
|
||||
branches(),
|
||||
branches_lock(),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
moveonenospc(false),
|
||||
direct_io(false),
|
||||
dropcacheonclose(false),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
nullrw(false),
|
||||
ignorepponrename(false),
|
||||
security_capability(true),
|
||||
link_cow(false),
|
||||
xattr(0),
|
||||
statfs(StatFS::BASE),
|
||||
statfs_ignore(StatFSIgnore::NONE),
|
||||
posix_acl(false),
|
||||
cache_symlinks(false),
|
||||
cache_readdir(false),
|
||||
async_read(true),
|
||||
cache_files(CacheFiles::LIBFUSE),
|
||||
fuse_msg_size(FUSE_MAX_MAX_PAGES),
|
||||
POLICYINIT(access),
|
||||
POLICYINIT(chmod),
|
||||
POLICYINIT(chown),
|
||||
POLICYINIT(create),
|
||||
POLICYINIT(getattr),
|
||||
POLICYINIT(getxattr),
|
||||
POLICYINIT(link),
|
||||
POLICYINIT(listxattr),
|
||||
POLICYINIT(mkdir),
|
||||
POLICYINIT(mknod),
|
||||
POLICYINIT(open),
|
||||
POLICYINIT(readlink),
|
||||
POLICYINIT(removexattr),
|
||||
POLICYINIT(rename),
|
||||
POLICYINIT(rmdir),
|
||||
POLICYINIT(setxattr),
|
||||
POLICYINIT(symlink),
|
||||
POLICYINIT(truncate),
|
||||
POLICYINIT(unlink),
|
||||
POLICYINIT(utimens),
|
||||
controlfile("/.mergerfs")
|
||||
{
|
||||
pthread_rwlock_init(&branches_lock,NULL);
|
||||
:
|
||||
open_cache(),
|
||||
|
||||
set_category_policy("action","epall");
|
||||
set_category_policy("create","epmfs");
|
||||
set_category_policy("search","ff");
|
||||
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),
|
||||
readfailover(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)
|
||||
{
|
||||
_map["async_read"] = &async_read;
|
||||
_map["auto_cache"] = &auto_cache;
|
||||
_map["branches"] = &branches;
|
||||
_map["cache.attr"] = &cache_attr;
|
||||
_map["cache.entry"] = &cache_entry;
|
||||
_map["cache.files"] = &cache_files;
|
||||
_map["cache.negative_entry"] = &cache_negative_entry;
|
||||
_map["cache.readdir"] = &cache_readdir;
|
||||
_map["cache.statfs"] = &cache_statfs;
|
||||
_map["cache.symlinks"] = &cache_symlinks;
|
||||
_map["cache.writeback"] = &writeback_cache;
|
||||
_map["category.action"] = &category.action;
|
||||
_map["category.create"] = &category.create;
|
||||
_map["category.search"] = &category.search;
|
||||
_map["direct_io"] = &direct_io;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["fsname"] = &fsname;
|
||||
_map["func.access"] = &func.access;
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
_map["func.chown"] = &func.chown;
|
||||
_map["func.create"] = &func.create;
|
||||
_map["func.getattr"] = &func.getattr;
|
||||
_map["func.getxattr"] = &func.getxattr;
|
||||
_map["func.link"] = &func.link;
|
||||
_map["func.listxattr"] = &func.listxattr;
|
||||
_map["func.mkdir"] = &func.mkdir;
|
||||
_map["func.mknod"] = &func.mknod;
|
||||
_map["func.open"] = &func.open;
|
||||
_map["func.readlink"] = &func.readlink;
|
||||
_map["func.removexattr"] = &func.removexattr;
|
||||
_map["func.rename"] = &func.rename;
|
||||
_map["func.rmdir"] = &func.rmdir;
|
||||
_map["func.setxattr"] = &func.setxattr;
|
||||
_map["func.symlink"] = &func.symlink;
|
||||
_map["func.truncate"] = &func.truncate;
|
||||
_map["func.unlink"] = &func.unlink;
|
||||
_map["func.utimens"] = &func.utimens;
|
||||
_map["fuse_msg_size"] = &fuse_msg_size;
|
||||
_map["ignorepponrename"] = &ignorepponrename;
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel_cache"] = &kernel_cache;
|
||||
_map["link_cow"] = &link_cow;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &mount;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
_map["nfsopenhack"] = &nfsopenhack;
|
||||
_map["nullrw"] = &nullrw;
|
||||
_map["pid"] = &pid;
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
// _map["readdir"] = &readdir;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["readfailover"] = &readfailover;
|
||||
_map["security_capability"] = &security_capability;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
_map["statfs_ignore"] = &statfs_ignore;
|
||||
_map["symlinkify"] = &symlinkify;
|
||||
_map["symlinkify_timeout"] = &symlinkify_timeout;
|
||||
_map["threads"] = &threads;
|
||||
_map["version"] = &version;
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
int
|
||||
Config::set_func_policy(const string &fusefunc_,
|
||||
const string &policy_)
|
||||
const
|
||||
Config&
|
||||
Config::ro(void)
|
||||
{
|
||||
const Policy *policy;
|
||||
const FuseFunc *fusefunc;
|
||||
|
||||
fusefunc = FuseFunc::find(fusefunc_);
|
||||
if(fusefunc == FuseFunc::invalid)
|
||||
return (errno=ENODATA,-1);
|
||||
|
||||
policy = Policy::find(policy_);
|
||||
if(policy == Policy::invalid)
|
||||
return (errno=EINVAL,-1);
|
||||
|
||||
policies[(FuseFunc::Enum::Type)*fusefunc] = policy;
|
||||
|
||||
return 0;
|
||||
return *((Config*)fuse_get_context()->private_data);
|
||||
}
|
||||
|
||||
int
|
||||
Config::set_category_policy(const string &category_,
|
||||
const string &policy_)
|
||||
Config&
|
||||
Config::rw(void)
|
||||
{
|
||||
const Policy *policy;
|
||||
const Category *category;
|
||||
|
||||
category = Category::find(category_);
|
||||
if(category == Category::invalid)
|
||||
return (errno=ENODATA,-1);
|
||||
|
||||
policy = Policy::find(policy_);
|
||||
if(policy == Policy::invalid)
|
||||
return (errno=EINVAL,-1);
|
||||
|
||||
for(int i = 0; i < FuseFunc::Enum::END; i++)
|
||||
{
|
||||
if(FuseFunc::fusefuncs[i] == (Category::Enum::Type)*category)
|
||||
policies[(FuseFunc::Enum::Type)FuseFunc::fusefuncs[i]] = policy;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Config::CacheFiles::operator int() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
Config::CacheFiles::operator std::string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case OFF:
|
||||
return "off";
|
||||
case PARTIAL:
|
||||
return "partial";
|
||||
case FULL:
|
||||
return "full";
|
||||
case AUTO_FULL:
|
||||
return "auto-full";
|
||||
case LIBFUSE:
|
||||
return "libfuse";
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Config::CacheFiles::CacheFiles()
|
||||
: _data(INVALID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Config::CacheFiles::CacheFiles(Config::CacheFiles::Enum data_)
|
||||
: _data(data_)
|
||||
{
|
||||
|
||||
return *((Config*)fuse_get_context()->private_data);
|
||||
}
|
||||
|
||||
bool
|
||||
Config::CacheFiles::valid() const
|
||||
Config::has_key(const std::string &key_) const
|
||||
{
|
||||
return (_data != INVALID);
|
||||
return _map.count(key_);
|
||||
}
|
||||
|
||||
Config::CacheFiles&
|
||||
Config::CacheFiles::operator=(const Config::CacheFiles::Enum data_)
|
||||
void
|
||||
Config::keys(std::string &s_) const
|
||||
{
|
||||
_data = data_;
|
||||
Str2TFStrMap::const_iterator i;
|
||||
Str2TFStrMap::const_iterator ei;
|
||||
|
||||
return *this;
|
||||
s_.reserve(512);
|
||||
|
||||
i = _map.begin();
|
||||
ei = _map.end();
|
||||
|
||||
for(; i != ei; ++i)
|
||||
{
|
||||
s_ += i->first;
|
||||
s_ += '\0';
|
||||
}
|
||||
|
||||
s_.resize(s_.size() - 1);
|
||||
}
|
||||
|
||||
Config::CacheFiles&
|
||||
Config::CacheFiles::operator=(const std::string &data_)
|
||||
{
|
||||
if(data_ == "off")
|
||||
_data = OFF;
|
||||
else if(data_ == "partial")
|
||||
_data = PARTIAL;
|
||||
else if(data_ == "full")
|
||||
_data = FULL;
|
||||
else if(data_ == "auto-full")
|
||||
_data = AUTO_FULL;
|
||||
else if(data_ == "libfuse")
|
||||
_data = LIBFUSE;
|
||||
else
|
||||
_data = INVALID;
|
||||
|
||||
return *this;
|
||||
void
|
||||
Config::keys_xattr(std::string &s_) const
|
||||
{
|
||||
Str2TFStrMap::const_iterator i;
|
||||
Str2TFStrMap::const_iterator ei;
|
||||
|
||||
s_.reserve(1024);
|
||||
for(i = _map.begin(), ei = _map.end(); i != ei; ++i)
|
||||
{
|
||||
s_ += "user.mergerfs.";
|
||||
s_ += i->first;
|
||||
s_ += '\0';
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Config::get(const std::string &key_,
|
||||
std::string *val_) const
|
||||
{
|
||||
Str2TFStrMap::const_iterator i;
|
||||
|
||||
i = _map.find(key_);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
|
||||
*val_ = i->second->to_string();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Config::set_raw(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
Str2TFStrMap::iterator i;
|
||||
|
||||
i = _map.find(key_);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
|
||||
return i->second->from_string(value_);
|
||||
}
|
||||
|
||||
int
|
||||
Config::set(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
if(l::readonly(key_))
|
||||
return -EINVAL;
|
||||
|
||||
return set_raw(key_,value_);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config &c_)
|
||||
{
|
||||
Str2TFStrMap::const_iterator i;
|
||||
Str2TFStrMap::const_iterator ei;
|
||||
|
||||
for(i = c_._map.begin(), ei = c_._map.end(); i != ei; ++i)
|
||||
{
|
||||
os_ << i->first << '=' << i->second << '\n';
|
||||
}
|
||||
|
||||
return os_;
|
||||
}
|
||||
|
||||
+77
-125
@@ -17,9 +17,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "fusefunc.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "config_moveonenospc.hpp"
|
||||
#include "config_nfsopenhack.hpp"
|
||||
#include "config_readdir.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 "tofrom_wrapper.hpp"
|
||||
|
||||
#include <fuse.h>
|
||||
|
||||
@@ -29,115 +41,17 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
typedef ToFromWrapper<uint64_t> ConfigUINT64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
struct StatFS
|
||||
{
|
||||
enum Enum
|
||||
{
|
||||
BASE,
|
||||
FULL
|
||||
};
|
||||
};
|
||||
|
||||
struct StatFSIgnore
|
||||
{
|
||||
enum Enum
|
||||
{
|
||||
NONE,
|
||||
RO,
|
||||
NC
|
||||
};
|
||||
};
|
||||
|
||||
class CacheFiles
|
||||
{
|
||||
public:
|
||||
enum Enum
|
||||
{
|
||||
INVALID = -1,
|
||||
LIBFUSE,
|
||||
OFF,
|
||||
PARTIAL,
|
||||
FULL,
|
||||
AUTO_FULL
|
||||
};
|
||||
|
||||
CacheFiles();
|
||||
CacheFiles(Enum);
|
||||
|
||||
operator int() const;
|
||||
operator std::string() const;
|
||||
|
||||
CacheFiles& operator=(const Enum);
|
||||
CacheFiles& operator=(const std::string&);
|
||||
|
||||
bool valid() const;
|
||||
|
||||
private:
|
||||
Enum _data;
|
||||
};
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
public:
|
||||
int set_func_policy(const std::string &fusefunc_,
|
||||
const std::string &policy_);
|
||||
int set_category_policy(const std::string &category_,
|
||||
const std::string &policy_);
|
||||
|
||||
public:
|
||||
std::string fsname;
|
||||
std::string destmount;
|
||||
Branches branches;
|
||||
mutable pthread_rwlock_t branches_lock;
|
||||
uint64_t minfreespace;
|
||||
bool moveonenospc;
|
||||
bool direct_io;
|
||||
bool kernel_cache;
|
||||
bool auto_cache;
|
||||
bool dropcacheonclose;
|
||||
bool symlinkify;
|
||||
time_t symlinkify_timeout;
|
||||
bool nullrw;
|
||||
bool ignorepponrename;
|
||||
bool security_capability;
|
||||
bool link_cow;
|
||||
int xattr;
|
||||
StatFS::Enum statfs;
|
||||
StatFSIgnore::Enum statfs_ignore;
|
||||
bool posix_acl;
|
||||
bool cache_symlinks;
|
||||
bool cache_readdir;
|
||||
bool async_read;
|
||||
CacheFiles cache_files;
|
||||
uint16_t fuse_msg_size;
|
||||
|
||||
public:
|
||||
const Policy *policies[FuseFunc::Enum::END];
|
||||
const Policy *&access;
|
||||
const Policy *&chmod;
|
||||
const Policy *&chown;
|
||||
const Policy *&create;
|
||||
const Policy *&getattr;
|
||||
const Policy *&getxattr;
|
||||
const Policy *&link;
|
||||
const Policy *&listxattr;
|
||||
const Policy *&mkdir;
|
||||
const Policy *&mknod;
|
||||
const Policy *&open;
|
||||
const Policy *&readlink;
|
||||
const Policy *&removexattr;
|
||||
const Policy *&rename;
|
||||
const Policy *&rmdir;
|
||||
const Policy *&setxattr;
|
||||
const Policy *&symlink;
|
||||
const Policy *&truncate;
|
||||
const Policy *&unlink;
|
||||
const Policy *&utimens;
|
||||
|
||||
public:
|
||||
mutable PolicyCache open_cache;
|
||||
|
||||
@@ -145,27 +59,65 @@ public:
|
||||
const std::string controlfile;
|
||||
|
||||
public:
|
||||
static
|
||||
const
|
||||
Config &
|
||||
get(void)
|
||||
{
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
ConfigBOOL async_read;
|
||||
ConfigBOOL auto_cache;
|
||||
Branches branches;
|
||||
ConfigUINT64 cache_attr;
|
||||
ConfigUINT64 cache_entry;
|
||||
CacheFiles cache_files;
|
||||
ConfigUINT64 cache_negative_entry;
|
||||
ConfigBOOL cache_readdir;
|
||||
ConfigUINT64 cache_statfs;
|
||||
ConfigBOOL cache_symlinks;
|
||||
FuncCategories category;
|
||||
ConfigBOOL direct_io;
|
||||
ConfigBOOL dropcacheonclose;
|
||||
ConfigSTR fsname;
|
||||
Funcs func;
|
||||
ConfigUINT64 fuse_msg_size;
|
||||
ConfigBOOL ignorepponrename;
|
||||
InodeCalc inodecalc;
|
||||
ConfigBOOL kernel_cache;
|
||||
ConfigBOOL link_cow;
|
||||
ConfigUINT64 minfreespace;
|
||||
ConfigSTR mount;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
ConfigBOOL nullrw;
|
||||
ConfigUINT64 pid;
|
||||
ConfigBOOL posix_acl;
|
||||
ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
ConfigBOOL readfailover;
|
||||
ConfigBOOL security_capability;
|
||||
SrcMounts srcmounts;
|
||||
StatFS statfs;
|
||||
StatFSIgnore statfs_ignore;
|
||||
ConfigBOOL symlinkify;
|
||||
ConfigUINT64 symlinkify_timeout;
|
||||
ConfigINT threads;
|
||||
ConfigSTR version;
|
||||
ConfigBOOL writeback_cache;
|
||||
XAttr xattr;
|
||||
|
||||
return get(fc);
|
||||
}
|
||||
public:
|
||||
friend std::ostream& operator<<(std::ostream &s,
|
||||
const Config &c);
|
||||
|
||||
static
|
||||
const Config &
|
||||
get(const fuse_context *fc)
|
||||
{
|
||||
return *((Config*)fc->private_data);
|
||||
}
|
||||
public:
|
||||
bool has_key(const std::string &key) const;
|
||||
void keys(std::string &s) const;
|
||||
void keys_xattr(std::string &s) const;
|
||||
|
||||
static
|
||||
Config &
|
||||
get_writable(void)
|
||||
{
|
||||
return (*((Config*)fuse_get_context()->private_data));
|
||||
}
|
||||
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);
|
||||
|
||||
public:
|
||||
static const Config &ro(void);
|
||||
static Config &rw(void);
|
||||
|
||||
private:
|
||||
Str2TFStrMap _map;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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_cachefiles.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
CacheFiles::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case CacheFiles::ENUM::LIBFUSE:
|
||||
return "libfuse";
|
||||
case CacheFiles::ENUM::OFF:
|
||||
return "off";
|
||||
case CacheFiles::ENUM::PARTIAL:
|
||||
return "partial";
|
||||
case CacheFiles::ENUM::FULL:
|
||||
return "full";
|
||||
case CacheFiles::ENUM::AUTO_FULL:
|
||||
return "auto-full";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
CacheFiles::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "libfuse")
|
||||
_data = CacheFiles::ENUM::LIBFUSE;
|
||||
ef(s_ == "off")
|
||||
_data = CacheFiles::ENUM::OFF;
|
||||
ef(s_ == "partial")
|
||||
_data = CacheFiles::ENUM::PARTIAL;
|
||||
ef(s_ == "full")
|
||||
_data = CacheFiles::ENUM::FULL;
|
||||
ef(s_ == "auto-full")
|
||||
_data = CacheFiles::ENUM::AUTO_FULL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
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 CacheFilesEnum
|
||||
{
|
||||
LIBFUSE,
|
||||
OFF,
|
||||
PARTIAL,
|
||||
FULL,
|
||||
AUTO_FULL
|
||||
};
|
||||
|
||||
typedef Enum<CacheFilesEnum> CacheFiles;
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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_inodecalc.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
|
||||
InodeCalc::InodeCalc(const std::string &s_)
|
||||
{
|
||||
fs::inode::set_algo(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
InodeCalc::to_string(void) const
|
||||
{
|
||||
return fs::inode::get_algo();
|
||||
}
|
||||
|
||||
int
|
||||
InodeCalc::from_string(const std::string &s_)
|
||||
{
|
||||
return fs::inode::set_algo(s_);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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 "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 &);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
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_moveonenospc.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
int
|
||||
MoveOnENOSPC::from_string(const std::string &s_)
|
||||
{
|
||||
int rv;
|
||||
std::string s;
|
||||
const Policy *tmp;
|
||||
|
||||
rv = str::from(s_,&enabled);
|
||||
if((rv == 0) && (enabled == true))
|
||||
s = "mfs";
|
||||
ef(rv != 0)
|
||||
s = s_;
|
||||
else
|
||||
return 0;
|
||||
|
||||
tmp = &Policy::find(s);
|
||||
if(tmp == Policy::invalid)
|
||||
return -EINVAL;
|
||||
|
||||
policy = tmp;
|
||||
enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
MoveOnENOSPC::to_string(void) const
|
||||
{
|
||||
if(enabled)
|
||||
return policy->to_string();
|
||||
return "false";
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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 "policy.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class MoveOnENOSPC : public ToFromString
|
||||
{
|
||||
public:
|
||||
MoveOnENOSPC(const bool enabled_)
|
||||
: enabled(enabled_)
|
||||
{
|
||||
policy = (enabled ?
|
||||
&Policy::mfs :
|
||||
&Policy::invalid);
|
||||
}
|
||||
|
||||
public:
|
||||
int from_string(const std::string &s);
|
||||
std::string to_string() const;
|
||||
|
||||
public:
|
||||
bool enabled;
|
||||
const Policy *policy;
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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_nfsopenhack.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
int
|
||||
NFSOpenHack::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "off")
|
||||
_data = NFSOpenHack::ENUM::OFF;
|
||||
ef(s_ == "git")
|
||||
_data = NFSOpenHack::ENUM::GIT;
|
||||
ef(s_ == "all")
|
||||
_data = NFSOpenHack::ENUM::ALL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string
|
||||
NFSOpenHack::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case NFSOpenHack::ENUM::OFF:
|
||||
return "off";
|
||||
case NFSOpenHack::ENUM::GIT:
|
||||
return "git";
|
||||
case NFSOpenHack::ENUM::ALL:
|
||||
return "all";
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
@@ -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 NFSOpenHackEnum
|
||||
{
|
||||
OFF,
|
||||
GIT,
|
||||
ALL
|
||||
};
|
||||
|
||||
typedef Enum<NFSOpenHackEnum> NFSOpenHack;
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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_readdir.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
int
|
||||
ReadDir::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "posix")
|
||||
_data = ReadDir::ENUM::POSIX;
|
||||
ef(s_ == "linux")
|
||||
_data = ReadDir::ENUM::LINUX;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string
|
||||
ReadDir::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case ReadDir::ENUM::POSIX:
|
||||
return "posix";
|
||||
case ReadDir::ENUM::LINUX:
|
||||
return "linux";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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 ReadDirEnum
|
||||
{
|
||||
POSIX,
|
||||
LINUX
|
||||
};
|
||||
|
||||
typedef Enum<ReadDirEnum> ReadDir;
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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_statfs.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
StatFS::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case StatFS::ENUM::BASE:
|
||||
return "base";
|
||||
case StatFS::ENUM::FULL:
|
||||
return "full";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
StatFS::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "base")
|
||||
_data = StatFS::ENUM::BASE;
|
||||
ef(s_ == "full")
|
||||
_data = StatFS::ENUM::FULL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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 StatFSEnum
|
||||
{
|
||||
BASE,
|
||||
FULL
|
||||
};
|
||||
|
||||
typedef Enum<StatFSEnum> StatFS;
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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_statfsignore.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
StatFSIgnore::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case StatFSIgnore::ENUM::NONE:
|
||||
return "none";
|
||||
case StatFSIgnore::ENUM::RO:
|
||||
return "ro";
|
||||
case StatFSIgnore::ENUM::NC:
|
||||
return "nc";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
StatFSIgnore::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "none")
|
||||
_data = StatFSIgnore::ENUM::NONE;
|
||||
ef(s_ == "ro")
|
||||
_data = StatFSIgnore::ENUM::RO;
|
||||
ef(s_ == "nc")
|
||||
_data = StatFSIgnore::ENUM::NC;
|
||||
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 StatFSIgnoreEnum
|
||||
{
|
||||
NONE,
|
||||
RO,
|
||||
NC
|
||||
};
|
||||
|
||||
typedef Enum<StatFSIgnoreEnum> StatFSIgnore;
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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_xattr.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
XAttr::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case XAttr::ENUM::PASSTHROUGH:
|
||||
return "passthrough";
|
||||
case XAttr::ENUM::NOSYS:
|
||||
return "nosys";
|
||||
case XAttr::ENUM::NOATTR:
|
||||
return "noattr";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
XAttr::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "passthrough")
|
||||
_data = XAttr::ENUM::PASSTHROUGH;
|
||||
ef(s_ == "nosys")
|
||||
_data = XAttr::ENUM::NOSYS;
|
||||
ef(s_ == "noattr")
|
||||
_data = XAttr::ENUM::NOATTR;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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"
|
||||
#include "errno.hpp"
|
||||
|
||||
enum class XAttrEnum
|
||||
{
|
||||
PASSTHROUGH = 0,
|
||||
NOSYS = ENOSYS,
|
||||
NOATTR = ENOATTR
|
||||
};
|
||||
|
||||
typedef Enum<XAttrEnum> XAttr;
|
||||
+4
-5
@@ -16,16 +16,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fh.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class DirInfo
|
||||
class DirInfo : public FH
|
||||
{
|
||||
public:
|
||||
DirInfo(const char *fusepath_)
|
||||
: fusepath(fusepath_)
|
||||
: FH(fusepath_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::string fusepath;
|
||||
};
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ef(X) else if(X)
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
template<typename E>
|
||||
class Enum : public ToFromString
|
||||
{
|
||||
public:
|
||||
typedef E ENUM;
|
||||
|
||||
public:
|
||||
Enum<ENUM>()
|
||||
{
|
||||
}
|
||||
|
||||
Enum<ENUM>(const ENUM data_)
|
||||
: _data(data_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Enum<ENUM>&
|
||||
operator=(const ENUM data_)
|
||||
{
|
||||
_data = data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Enum<ENUM>&
|
||||
operator=(const std::string &s_)
|
||||
{
|
||||
from_string(s_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
operator ENUM() const
|
||||
{
|
||||
return _data;
|
||||
}
|
||||
|
||||
public:
|
||||
bool operator==(const ENUM data_) const
|
||||
{
|
||||
return (_data == data_);
|
||||
}
|
||||
|
||||
public:
|
||||
std::string to_string() const;
|
||||
int from_string(const std::string &);
|
||||
|
||||
public:
|
||||
int to_int() const
|
||||
{
|
||||
return (int)_data;
|
||||
}
|
||||
|
||||
private:
|
||||
ENUM _data;
|
||||
};
|
||||
@@ -1,84 +0,0 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com)
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "fasthash.h"
|
||||
|
||||
// Compression function for Merkle-Damgard construction.
|
||||
// This function is generated using the framework provided.
|
||||
#define mix(h) ({ \
|
||||
(h) ^= (h) >> 23; \
|
||||
(h) *= 0x2127599bf4325c37ULL; \
|
||||
(h) ^= (h) >> 47; })
|
||||
|
||||
uint64_t
|
||||
fasthash64(const void *buf,
|
||||
size_t len,
|
||||
uint64_t seed)
|
||||
{
|
||||
const uint64_t m = 0x880355f21e6d1965ULL;
|
||||
const uint64_t *pos = (const uint64_t *)buf;
|
||||
const uint64_t *end = pos + (len / 8);
|
||||
const unsigned char *pos2;
|
||||
uint64_t h = seed ^ (len * m);
|
||||
uint64_t v;
|
||||
|
||||
while (pos != end) {
|
||||
v = *pos++;
|
||||
h ^= mix(v);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
pos2 = (const unsigned char*)pos;
|
||||
v = 0;
|
||||
|
||||
switch (len & 7) {
|
||||
case 7: v ^= (uint64_t)pos2[6] << 48;
|
||||
case 6: v ^= (uint64_t)pos2[5] << 40;
|
||||
case 5: v ^= (uint64_t)pos2[4] << 32;
|
||||
case 4: v ^= (uint64_t)pos2[3] << 24;
|
||||
case 3: v ^= (uint64_t)pos2[2] << 16;
|
||||
case 2: v ^= (uint64_t)pos2[1] << 8;
|
||||
case 1: v ^= (uint64_t)pos2[0];
|
||||
h ^= mix(v);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
return mix(h);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
fasthash32(const void *buf,
|
||||
size_t len,
|
||||
uint32_t seed)
|
||||
{
|
||||
// the following trick converts the 64-bit hashcode to Fermat
|
||||
// residue, which shall retain information from both the higher
|
||||
// and lower parts of hashcode.
|
||||
uint64_t h;
|
||||
|
||||
h = fasthash64(buf, len, seed);
|
||||
|
||||
return (h - (h >> 32));
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (C) 2012 Zilong Tan (eric.zltan@gmail.com)
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef _FASTHASH_H
|
||||
#define _FASTHASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fasthash32 - 32-bit implementation of fasthash
|
||||
* @buf: data buffer
|
||||
* @len: data size
|
||||
* @seed: the seed
|
||||
*/
|
||||
uint32_t fasthash32(const void *buf, size_t len, uint32_t seed);
|
||||
|
||||
/**
|
||||
* fasthash64 - 64-bit implementation of fasthash
|
||||
* @buf: data buffer
|
||||
* @len: data size
|
||||
* @seed: the seed
|
||||
*/
|
||||
uint64_t fasthash64(const void *buf, size_t len, uint64_t seed);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
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 <string>
|
||||
|
||||
class FH
|
||||
{
|
||||
public:
|
||||
FH(const char *fusepath_)
|
||||
: fusepath(fusepath_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::string fusepath;
|
||||
};
|
||||
+9
-5
@@ -16,19 +16,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fh.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class FileInfo
|
||||
class FileInfo : public FH
|
||||
{
|
||||
public:
|
||||
FileInfo(const int fd_,
|
||||
const char *fusepath_)
|
||||
: fd(fd_),
|
||||
fusepath(fusepath_)
|
||||
const char *fusepath_,
|
||||
const int flags_)
|
||||
: FH(fusepath_),
|
||||
fd(fd_),
|
||||
flags(flags_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
int fd;
|
||||
std::string fusepath;
|
||||
int flags;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fixed_mem_pool_t fixed_mem_pool_t;
|
||||
struct fixed_mem_pool_t
|
||||
{
|
||||
fixed_mem_pool_t *next;
|
||||
};
|
||||
|
||||
template<uint64_t SIZE>
|
||||
class FixedMemPool
|
||||
{
|
||||
public:
|
||||
FixedMemPool()
|
||||
{
|
||||
list.next = NULL;
|
||||
}
|
||||
|
||||
~FixedMemPool()
|
||||
{
|
||||
void *mem;
|
||||
while(!empty())
|
||||
{
|
||||
mem = alloc();
|
||||
::free(mem);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
empty(void)
|
||||
{
|
||||
return (list.next == NULL);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
size(void)
|
||||
{
|
||||
return SIZE;
|
||||
}
|
||||
|
||||
void*
|
||||
alloc(void)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
if(list.next == NULL)
|
||||
return malloc(SIZE);
|
||||
|
||||
rv = (void*)list.next;
|
||||
list.next = list.next->next;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
free(void *mem_)
|
||||
{
|
||||
fixed_mem_pool_t *next;
|
||||
|
||||
next = (fixed_mem_pool_t*)mem_;
|
||||
next->next = list.next;
|
||||
list.next = next;
|
||||
}
|
||||
|
||||
private:
|
||||
fixed_mem_pool_t list;
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ef.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace str
|
||||
{
|
||||
int
|
||||
from(const std::string &value_,
|
||||
bool *bool_)
|
||||
{
|
||||
if((value_ == "true") ||
|
||||
(value_ == "1") ||
|
||||
(value_ == "on") ||
|
||||
(value_ == "yes"))
|
||||
*bool_ = true;
|
||||
ef((value_ == "false") ||
|
||||
(value_ == "0") ||
|
||||
(value_ == "off") ||
|
||||
(value_ == "no"))
|
||||
*bool_ = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
from(const std::string &value_,
|
||||
int *int_)
|
||||
{
|
||||
*int_ = ::strtol(value_.c_str(),NULL,10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
from(const std::string &value_,
|
||||
uint64_t *uint64_)
|
||||
{
|
||||
char *endptr;
|
||||
uint64_t tmp;
|
||||
|
||||
tmp = ::strtoll(value_.c_str(),&endptr,10);
|
||||
switch(*endptr)
|
||||
{
|
||||
case 'k':
|
||||
case 'K':
|
||||
tmp *= 1024ULL;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
case 'M':
|
||||
tmp *= (1024ULL * 1024ULL);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case 'G':
|
||||
tmp *= (1024ULL * 1024ULL * 1024ULL);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
case 'T':
|
||||
tmp *= (1024ULL * 1024ULL * 1024ULL * 1024ULL);
|
||||
break;
|
||||
|
||||
case '\0':
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*uint64_ = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
from(const std::string &value_,
|
||||
std::string *str_)
|
||||
{
|
||||
*str_ = value_;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
from(const std::string &value_,
|
||||
const std::string *key_)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace str
|
||||
{
|
||||
int from(const std::string &, bool *);
|
||||
int from(const std::string &, int *);
|
||||
int from(const std::string &, uint64_t *);
|
||||
int from(const std::string &, std::string *);
|
||||
int from(const std::string &, const std::string *);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user