mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-04-21 12:31:43 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61e31eecf6 | ||
|
|
ac1f4b4d52 | ||
|
|
ddf5e53aa4 | ||
|
|
a2a12afb34 | ||
|
|
7606430188 | ||
|
|
629806e896 | ||
|
|
50fbf14e1e | ||
|
|
4a67bbdc71 | ||
|
|
dba9c4451a | ||
|
|
fea499e220 | ||
|
|
9d056d609f | ||
|
|
9a839dd8ba | ||
|
|
32e2c9c48e | ||
|
|
83d7886a0a | ||
|
|
316abba0ac | ||
|
|
8001c58c36 | ||
|
|
3b0d7fb26e | ||
|
|
5d3c5002e0 | ||
|
|
9082285cc8 | ||
|
|
d1762b2bac | ||
|
|
46ce28b149 | ||
|
|
faadeb75e0 | ||
|
|
338e559e53 | ||
|
|
3db673410f | ||
|
|
77f098614f | ||
|
|
9589846e9a | ||
|
|
e2dda3c1b1 | ||
|
|
d9ab18282f | ||
|
|
14c2ff9ab8 | ||
|
|
77e6f400b5 | ||
|
|
b95ff8ba3a | ||
|
|
4bf8738e5c | ||
|
|
3f060f4512 | ||
|
|
c4c35a9ad0 | ||
|
|
c9a935835f | ||
|
|
015002ebd9 | ||
|
|
9ca10b2413 | ||
|
|
2d5c2c2b6e | ||
|
|
e3ab739a56 | ||
|
|
c784d06029 | ||
|
|
58803f4845 | ||
|
|
f159fe61f9 | ||
|
|
c43b464d6c | ||
|
|
34c684a4f5 | ||
|
|
e256c88e4c | ||
|
|
518b4a6b85 | ||
|
|
ee8532c70d | ||
|
|
c58da1ab9c | ||
|
|
68b3026264 | ||
|
|
810c5a40f7 | ||
|
|
18dead4d86 | ||
|
|
2082357d3a | ||
|
|
68719da828 | ||
|
|
31fd1ec82e | ||
|
|
c3845097ee | ||
|
|
d76934ee9d | ||
|
|
5f737cb7bf | ||
|
|
50ede1ba97 | ||
|
|
930dad31de | ||
|
|
88b91161e0 | ||
|
|
8150957a01 | ||
|
|
fee97efafb | ||
|
|
80856cf7ac | ||
|
|
c929781d08 | ||
|
|
313aa2a1d3 | ||
|
|
021ce3d97c | ||
|
|
4ea0de3ef2 | ||
|
|
6b5c484fbf | ||
|
|
b165332486 | ||
|
|
f598d3bc38 | ||
|
|
4daa9e4eb9 | ||
|
|
122f9c1d5d | ||
|
|
2da5cda4b3 | ||
|
|
5263a65dcf | ||
|
|
1255d3d1b2 | ||
|
|
8def16a811 | ||
|
|
7d1683b607 | ||
|
|
43a6d66e3c | ||
|
|
8b33f9a766 | ||
|
|
df721eb253 | ||
|
|
0547517a99 | ||
|
|
54e85007a2 | ||
|
|
6e53f3b698 | ||
|
|
1aaf742c13 | ||
|
|
3754e20049 | ||
|
|
b4c031d1f0 | ||
|
|
3b8d495c43 | ||
|
|
61c2187c14 | ||
|
|
192e4a4af8 | ||
|
|
749f0290e9 | ||
|
|
7013ff9aeb | ||
|
|
3ce0dc56e4 | ||
|
|
8e47bf4946 | ||
|
|
098f353bf1 | ||
|
|
859b4da3dc | ||
|
|
80f30999fa | ||
|
|
3fdd322a5c | ||
|
|
c40f42e57e | ||
|
|
18e87980a4 | ||
|
|
44efbaabe6 | ||
|
|
a02f3a6856 | ||
|
|
9573a053d6 | ||
|
|
9a5d691619 | ||
|
|
495afeffb1 | ||
|
|
4acc1defe2 | ||
|
|
58b4eb9089 | ||
|
|
ca4b4c6d35 | ||
|
|
9a90609428 | ||
|
|
3e691149f5 | ||
|
|
8adebc9489 | ||
|
|
50daf84ec0 | ||
|
|
d337574665 | ||
|
|
39d98f1eac | ||
|
|
cbea2ba91b | ||
|
|
f6e37672b8 | ||
|
|
55bd9a06b1 | ||
|
|
6a9f7e6afc | ||
|
|
f2158d3e66 | ||
|
|
3bc189cfc8 | ||
|
|
d0c5ff7f87 | ||
|
|
dfb544fed8 | ||
|
|
ae893ca051 | ||
|
|
538467b86d | ||
|
|
46de43d4ad | ||
|
|
3808e2683e | ||
|
|
52342a76c1 | ||
|
|
e9e17baf5e | ||
|
|
2e4c6c5fd1 | ||
|
|
f50812a9ce | ||
|
|
861edff49a | ||
|
|
5e08357f0e | ||
|
|
411ee36c90 | ||
|
|
44a98910ba | ||
|
|
8a335d8a6e | ||
|
|
3900543970 | ||
|
|
2e34c7598a | ||
|
|
7edd3c6cf3 | ||
|
|
ddf0075938 | ||
|
|
1b26f4908e | ||
|
|
2d8fab534a | ||
|
|
c3fffefbc1 | ||
|
|
eaa2593a4b | ||
|
|
7e583e3f48 | ||
|
|
2ad4891617 | ||
|
|
8990e24c0f | ||
|
|
88b80234c7 | ||
|
|
f6e37c57fe | ||
|
|
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 |
+254
-29
@@ -1,3 +1,13 @@
|
||||
freebsd_task:
|
||||
name: "freebsd:11.4"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-11-4
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
name: "freebsd:12.1"
|
||||
freebsd_instance:
|
||||
@@ -6,7 +16,35 @@ freebsd_task:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
name: "freebsd:12.2"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-2
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
freebsd_task:
|
||||
name: "freebsd:13.0"
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-0
|
||||
env:
|
||||
ASSUME_ALWAYS_YES: yes
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- gmake -j4
|
||||
|
||||
|
||||
#macos_task:
|
||||
# osx_instance:
|
||||
# image: catalina-base
|
||||
# script:
|
||||
# - tools/install-build-pkgs
|
||||
# - gmake -j4
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.11"
|
||||
@@ -20,16 +58,59 @@ linux_task:
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "centos:6"
|
||||
name: "alpine:3.12"
|
||||
container:
|
||||
image: centos:6
|
||||
image: alpine:3.12
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.13"
|
||||
container:
|
||||
image: alpine:3.13
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:3.14"
|
||||
container:
|
||||
image: alpine:3.14
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine:latest"
|
||||
container:
|
||||
image: alpine:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "alpine-arm:latest"
|
||||
arm_container:
|
||||
image: alpine:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make STATIC=1 LTO=1
|
||||
|
||||
linux_task:
|
||||
name: "centos:7"
|
||||
@@ -56,39 +137,93 @@ linux_task:
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:20.04"
|
||||
name: "fedora:30"
|
||||
container:
|
||||
image: ubuntu:20.04
|
||||
image: fedora:30
|
||||
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
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:19.10"
|
||||
name: "fedora:31"
|
||||
container:
|
||||
image: ubuntu:19.10
|
||||
image: fedora:31
|
||||
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
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:18.04"
|
||||
name: "fedora:32"
|
||||
container:
|
||||
image: ubuntu:18.04
|
||||
image: fedora:32
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:33"
|
||||
container:
|
||||
image: fedora:33
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:34"
|
||||
container:
|
||||
image: fedora:34
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora:latest"
|
||||
container:
|
||||
image: fedora:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "fedora-arm:latest"
|
||||
arm_container:
|
||||
image: fedora:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- make
|
||||
- make rpm
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:14.04"
|
||||
container:
|
||||
image: ubuntu:14.04
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
@@ -116,9 +251,9 @@ linux_task:
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu:14.04"
|
||||
name: "ubuntu:18.04"
|
||||
container:
|
||||
image: ubuntu:14.04
|
||||
image: ubuntu:18.04
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
@@ -131,9 +266,9 @@ linux_task:
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:10"
|
||||
name: "ubuntu:20.04"
|
||||
container:
|
||||
image: debian:10
|
||||
image: ubuntu:20.04
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
@@ -146,9 +281,39 @@ linux_task:
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:9"
|
||||
name: "ubuntu:latest"
|
||||
container:
|
||||
image: debian:9
|
||||
image: ubuntu:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "ubuntu-arm:latest"
|
||||
arm_container:
|
||||
image: ubuntu:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
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
|
||||
@@ -176,9 +341,69 @@ linux_task:
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:7"
|
||||
name: "debian:9"
|
||||
container:
|
||||
image: debian:8
|
||||
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
|
||||
|
||||
linux_task:
|
||||
name: "debian:11"
|
||||
container:
|
||||
image: debian:11
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian:latest"
|
||||
container:
|
||||
image: debian:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
script:
|
||||
- tools/install-build-pkgs
|
||||
- git fetch
|
||||
- make deb
|
||||
- apt-get -y install fuse
|
||||
- dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
|
||||
linux_task:
|
||||
name: "debian-arm:latest"
|
||||
arm_container:
|
||||
image: debian:latest
|
||||
cpu: 4
|
||||
memory: 2G
|
||||
timeout_in: 10m
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [trapexit] # [user]
|
||||
github: [trapexit]
|
||||
patreon: trapexit
|
||||
open_collective: trapexit
|
||||
custom: ['https://paypal.me/trapexit','https://buymeacoff.ee/trapexit']
|
||||
ko_fi: trapexit
|
||||
custom: ['https://www.subscribestar.com/trapexit','https://paypal.me/trapexit']
|
||||
open_collective: trapexit
|
||||
|
||||
@@ -8,21 +8,42 @@ assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. Please do not file bugs for unreleased versions. The master branch is **not** to be considered in a production state.
|
||||
|
||||
A clear and concise description of the unexpected behavior.
|
||||
|
||||
**Please be sure to use latest release of mergerfs to ensure the issue still exists. Not your distro's latest but the latest official release.**
|
||||
|
||||
The master branch is **not** to be considered production ready. Feel free to file bug reports but do so indicating clearly that you are testing unreleased code.
|
||||
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior. List **all** steps to reproduce. All settings.
|
||||
|
||||
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
|
||||
- Reproduce with standard tooling if possible (touch,truncate,rm,rmdir,ln,etc.) Having to install 3rd party software will make debugging more difficult.
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
A clear and concise description of the expected behavior.
|
||||
|
||||
|
||||
**System information:**
|
||||
|
||||
- OS, kernel version: `uname -a`
|
||||
- mergerfs version: `mergerfs -V`
|
||||
- mergerfs settings
|
||||
- List of drives, filesystems, & sizes: `df -h`
|
||||
- A strace of the application having a problem: `strace -f -o /tmp/app.strace.txt <cmd>` or `strace -f -p <appPID> -o /tmp/app.strace.txt`
|
||||
- strace of mergerfs while app tried to do it's thing: `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
- List of drives, filesystems, & sizes:
|
||||
- `df -h`
|
||||
- `lsblk -f`
|
||||
- A strace of the application having a problem:
|
||||
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
|
||||
- `strace -fvTtt -s 256 -o /tmp/app.strace.txt -p <appPID>`
|
||||
- strace of mergerfs while app tried to do it's thing:
|
||||
- `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
|
||||
**Additional context**
|
||||
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: Question
|
||||
about: When you're looking for information
|
||||
about: When you're looking for information or general support
|
||||
title: ''
|
||||
labels: investigating, question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
You can also ask questions on Discord for realtime help: https://discord.gg/MpAr69V
|
||||
For general questions and support please use [GitHub Discussions](https://github.com/trapexit/mergerfs/discussions) or [Discord](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`
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "7 3 * * 6"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ python, cpp ]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
if: ${{ matrix.language == 'python' || matrix.language == 'cpp' }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: precise
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: precise
|
||||
compiler: clang
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: clang
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: gcc
|
||||
sudo: required
|
||||
- os: linux
|
||||
dist: bionic
|
||||
compiler: clang
|
||||
sudo: required
|
||||
|
||||
script:
|
||||
- sudo -E apt-get install fakeroot
|
||||
- sudo -E make install-build-pkgs
|
||||
- make -j$(($(nproc) + 1))
|
||||
- make deb
|
||||
- sudo -E dpkg -i ../*.deb
|
||||
- mergerfs -v || true
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
@@ -40,7 +40,7 @@ USE_XATTR = 1
|
||||
UGID_USE_RWLOCK = 0
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
@@ -58,16 +58,24 @@ LTO_FLAGS :=
|
||||
endif
|
||||
|
||||
SRC = $(wildcard src/*.cpp)
|
||||
OBJS = $(SRC:src/%.cpp=build/%.o)
|
||||
DEPS = $(SRC:src/%.cpp=build/%.d)
|
||||
OBJS = $(SRC:src/%.cpp=build/.src/%.o)
|
||||
DEPS = $(SRC:src/%.cpp=build/.src/%.d)
|
||||
|
||||
TESTS = $(wildcard tests/*.cpp)
|
||||
TESTS_OBJS = $(filter-out build/.src/mergerfs.o,$(OBJS))
|
||||
TESTS_OBJS += $(TESTS:tests/%.cpp=build/.tests/%.o)
|
||||
TESTS_DEPS = $(TESTS:tests/%.cpp=build/.tests/%.d)
|
||||
TESTS_DEPS += $(DEPS)
|
||||
|
||||
MANPAGE = mergerfs.1
|
||||
CXXFLAGS = \
|
||||
-std=c++0x \
|
||||
$(OPT_FLAGS) \
|
||||
CXXFLAGS ?= ${OPT_FLAGS}
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++11 \
|
||||
$(STATIC_FLAGS) \
|
||||
$(LTO_FLAGS) \
|
||||
-Wall \
|
||||
-Wno-unused-result \
|
||||
-Wno-unused-result \
|
||||
-MMD
|
||||
FUSE_FLAGS = \
|
||||
-Ilibfuse/include \
|
||||
@@ -76,9 +84,14 @@ FUSE_FLAGS = \
|
||||
MFS_FLAGS = \
|
||||
-DUSE_XATTR=$(USE_XATTR) \
|
||||
-DUGID_USE_RWLOCK=$(UGID_USE_RWLOCK)
|
||||
LDFLAGS = \
|
||||
-pthread \
|
||||
-lrt
|
||||
TESTS_FLAGS = \
|
||||
-Isrc \
|
||||
-DTESTS
|
||||
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-pthread \
|
||||
-lrt
|
||||
|
||||
DESTDIR =
|
||||
PREFIX = /usr/local
|
||||
@@ -107,11 +120,19 @@ help:
|
||||
objects: version build/stamp
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
tests-objects:
|
||||
$(MAKE) $(TESTS_OBJS)
|
||||
|
||||
build/mergerfs: libfuse objects
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
build/tests: build/mergerfs tests-objects
|
||||
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) $(TESTS_OBJS) -o $@ libfuse/build/libfuse.a $(LDFLAGS)
|
||||
|
||||
mergerfs: build/mergerfs
|
||||
|
||||
tests: build/tests
|
||||
|
||||
changelog:
|
||||
ifeq ($(GIT_REPO),1)
|
||||
$(GIT2DEBCL) --name mergerfs > ChangeLog
|
||||
@@ -124,12 +145,16 @@ version:
|
||||
tools/update-version
|
||||
|
||||
build/stamp:
|
||||
$(MKDIR) -p build
|
||||
$(MKDIR) -p build/.src build/.tests
|
||||
$(TOUCH) $@
|
||||
|
||||
build/%.o: src/%.cpp
|
||||
build/.src/%.o: src/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
build/.tests/%.o: tests/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(TESTS_FLAGS) $(FUSE_FLAGS) $(MFS_FLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean: rpm-clean
|
||||
$(RM) -rf build
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
% mergerfs(1) mergerfs user manual
|
||||
% Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
% 2020-08-03
|
||||
% 2022-05-23
|
||||
|
||||
# NAME
|
||||
|
||||
@@ -19,19 +19,19 @@ mergerfs -o<options> <branches> <mountpoint>
|
||||
|
||||
# FEATURES
|
||||
|
||||
* Runs in userspace (FUSE)
|
||||
* Configurable behaviors / file placement
|
||||
* Ability to add or remove filesystems at will
|
||||
* Resistance to individual filesystem failure
|
||||
* Support for extended attributes (xattrs)
|
||||
* Support for file attributes (chattr)
|
||||
* Runtime configurable (via xattrs)
|
||||
* Safe to run as root
|
||||
* Opportunistic credential caching
|
||||
* Works with heterogeneous filesystem types
|
||||
* Handling of writes to full drives (transparently move file to drive with capacity)
|
||||
* Handles pool of read-only and read/write drives
|
||||
* Can turn read-only files into symlinks to underlying file
|
||||
* Moving of file when filesystem runs out of space while writing
|
||||
* Ignore read-only filesystems when creating files
|
||||
* Turn read-only files into symlinks to underlying file
|
||||
* Hard link copy-on-write / CoW
|
||||
* supports POSIX ACLs
|
||||
* Support for POSIX ACLs
|
||||
* Misc other things
|
||||
|
||||
|
||||
# HOW IT WORKS
|
||||
@@ -59,7 +59,7 @@ A + B = C
|
||||
+-- file6
|
||||
```
|
||||
|
||||
mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **not** split data across drives. It is not RAID0 / striping. It is simply a union.
|
||||
mergerfs does **NOT** support the copy-on-write (CoW) or whiteout behaviors found in **aufs** and **overlayfs**. You can **not** mount a read-only filesystem and write to it. However, mergerfs will ignore read-only drives when creating new files so you can mix read-write and read-only drives. It also does **NOT** split data across drives. It is not RAID0 / striping. It is simply a union of other filesystems.
|
||||
|
||||
|
||||
# TERMINOLOGY
|
||||
@@ -67,9 +67,9 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
|
||||
* branch: A base path used in the pool.
|
||||
* pool: The mergerfs mount. The union of the branches.
|
||||
* relative path: The path in the pool relative to the branch and mount.
|
||||
* function: A filesystem call (open, unlink, create, getattr, rmdir, etc.)
|
||||
* category: A collection of functions based on basic behavior (action, create, search).
|
||||
* policy: The algorithm used to select a file when performing a function.
|
||||
* function: A filesystem call (open, unlink, create, getattr, etc.)
|
||||
* category: A collection of functions (action, create, search).
|
||||
* path preservation: Aspect of some policies which includes checking the path for which a file would be created.
|
||||
|
||||
|
||||
@@ -77,31 +77,34 @@ mergerfs does **not** support the copy-on-write (CoW) behavior found in **aufs**
|
||||
|
||||
If you don't already know that you have a special use case then just start with one of the following option sets.
|
||||
|
||||
#### You need `mmap` (used by rtorrent and many sqlite3 base software)
|
||||
|
||||
`allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs`
|
||||
|
||||
#### You don't need `mmap`
|
||||
|
||||
`use_ino,cache.files=off,dropcacheonclose=true,allow_other,category.create=mfs`
|
||||
`allow_other,use_ino,cache.files=off,dropcacheonclose=true,category.create=mfs`
|
||||
|
||||
#### You do need `mmap` (used by rtorrent and some other programs)
|
||||
|
||||
`use_ino,cache.files=partial,dropcacheonclose=true,allow_other,category.create=mfs`
|
||||
|
||||
See the mergerfs [wiki for real world deployments](https://github.com/trapexit/mergerfs/wiki/Real-World-Deployments) for comparisons / ideas.
|
||||
|
||||
|
||||
# OPTIONS
|
||||
|
||||
These options are the same regardless you use them with the `mergerfs` commandline program, used in fstab, or in a config file.
|
||||
|
||||
### mount options
|
||||
|
||||
* **config**: Path to a config file. Same arguments as below in key=val format.
|
||||
* **config**: Path to a config file. Same arguments as below in key=val / ini style format.
|
||||
* **branches**: Colon delimited list of branches.
|
||||
* **allow_other**: A libfuse option which allows users besides the one which ran mergerfs to see the filesystem. This is required for most use-cases.
|
||||
* **minfreespace=SIZE**: The minimum space value used for creation policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
|
||||
* **minfreespace=SIZE**: The minimum space value used for creation policies. Can be overridden by branch specific option. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
|
||||
* **moveonenospc=BOOL|POLICY**: When enabled if a **write** fails with **ENOSPC** (no space left on device) or **EDQUOT** (disk quota exceeded) the policy selected will run to find a new location for the file. An attempt to move the file to that branch will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false, true = mfs)
|
||||
* **use_ino**: Causes mergerfs to supply file/directory inodes rather than libfuse. While not a default it is recommended it be enabled so that linked files share the same inode value.
|
||||
* **inodecalc=passthrough|path-hash|devino-hash|hybrid-hash**: Selects the inode calculation algorithm. (default: hybrid-hash)
|
||||
* **dropcacheonclose=BOOL**: When a file is requested to be closed call `posix_fadvise` on it first to instruct the kernel that we no longer need the data and it can drop its cache. Recommended when **cache.files=partial|full|auto-full** to limit double caching. (default: false)
|
||||
* **symlinkify=BOOL**: When enabled and a file is not writable and its mtime or ctime is older than **symlinkify_timeout** files will be reported as symlinks to the original files. Please read more below before using. (default: false)
|
||||
* **symlinkify_timeout=INT**: Time to wait, in seconds, to activate the **symlinkify** behavior. (default: 3600)
|
||||
* **symlinkify_timeout=UINT**: Time to wait, in seconds, to activate the **symlinkify** behavior. (default: 3600)
|
||||
* **nullrw=BOOL**: Turns reads and writes into no-ops. The request will succeed but do nothing. Useful for benchmarking mergerfs. (default: false)
|
||||
* **ignorepponrename=BOOL**: Ignore path preserving on rename. Typically rename and link act differently depending on the policy of `create` (read below). Enabling this will cause rename and link to always use the non-path preserving behavior. This means files, when renamed or linked, will stay on the same drive. (default: false)
|
||||
* **security_capability=BOOL**: If false return ENOATTR when xattr security.capability is queried. (default: true)
|
||||
@@ -110,18 +113,23 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
|
||||
* **statfs=base|full**: Controls how statfs works. 'base' means it will always use all branches in statfs calculations. 'full' is in effect path preserving and only includes drives where the path exists. (default: base)
|
||||
* **statfs_ignore=none|ro|nc**: 'ro' will cause statfs calculations to ignore available space for branches mounted or tagged as 'read-only' or 'no create'. 'nc' will ignore available space for branches tagged as 'no create'. (default: none)
|
||||
* **nfsopenhack=off|git|all**: A workaround for exporting mergerfs over NFS where there are issues with creating files for write while setting the mode to read-only. (default: off)
|
||||
* **follow-symlinks=never|directory|regular|all**: Turns symlinks into what they point to. (default: never)
|
||||
* **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**: When a link fails with EXDEV optionally create a symlink to the file instead.
|
||||
* **rename-exdev=passthrough|rel-symlink|abs-symlink**: When a rename fails with EXDEV optionally move the file to a special directory and symlink to it.
|
||||
* **posix_acl=BOOL**: Enable POSIX ACL support (if supported by kernel and underlying filesystem). (default: false)
|
||||
* **async_read=BOOL**: Perform reads asynchronously. If disabled or unavailable the kernel will ensure there is at most one pending read request per file handle and will attempt to order requests by offset. (default: true)
|
||||
* **fuse_msg_size=INT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
|
||||
* **fuse_msg_size=UINT**: Set the max number of pages per FUSE message. Only available on Linux >= 4.20 and ignored otherwise. (min: 1; max: 256; default: 256)
|
||||
* **threads=INT**: Number of threads to use in multithreaded mode. When set to zero it will attempt to discover and use the number of logical cores. If the lookup fails it will fall back to using 4. If the thread count is set negative it will look up the number of cores then divide by the absolute value. ie. threads=-2 on an 8 core machine will result in 8 / 2 = 4 threads. There will always be at least 1 thread. NOTE: higher number of threads increases parallelism but usually decreases throughput. (default: 0)
|
||||
* **fsname=STR**: Sets the name of the filesystem as seen in **mount**, **df**, etc. Defaults to a list of the source paths concatenated together with the longest common prefix removed.
|
||||
* **func.FUNC=POLICY**: Sets the specific FUSE function's policy. See below for the list of value types. Example: **func.getattr=newest**
|
||||
* **category.CATEGORY=POLICY**: Sets policy of all FUSE functions in the provided category. See POLICIES section for defaults. Example: **category.create=mfs**
|
||||
* **cache.open=INT**: 'open' policy cache timeout in seconds. (default: 0)
|
||||
* **cache.statfs=INT**: 'statfs' cache timeout in seconds. (default: 0)
|
||||
* **cache.attr=INT**: File attribute cache timeout in seconds. (default: 1)
|
||||
* **cache.entry=INT**: File name lookup cache timeout in seconds. (default: 1)
|
||||
* **cache.negative_entry=INT**: Negative file name lookup cache timeout in seconds. (default: 0)
|
||||
* **category.action=POLICY**: Sets policy of all FUSE functions in the action category. (default: epall)
|
||||
* **category.create=POLICY**: Sets policy of all FUSE functions in the create category. (default: epmfs)
|
||||
* **category.search=POLICY**: Sets policy of all FUSE functions in the search category. (default: ff)
|
||||
* **cache.open=UINT**: 'open' policy cache timeout in seconds. (default: 0)
|
||||
* **cache.statfs=UINT**: 'statfs' cache timeout in seconds. (default: 0)
|
||||
* **cache.attr=UINT**: File attribute cache timeout in seconds. (default: 1)
|
||||
* **cache.entry=UINT**: File name lookup cache timeout in seconds. (default: 1)
|
||||
* **cache.negative_entry=UINT**: Negative file name lookup cache timeout in seconds. (default: 0)
|
||||
* **cache.files=libfuse|off|partial|full|auto-full**: File page caching mode (default: libfuse)
|
||||
* **cache.writeback=BOOL**: Enable kernel writeback caching (default: false)
|
||||
* **cache.symlinks=BOOL**: Cache symlinks (if supported by kernel) (default: false)
|
||||
@@ -139,21 +147,38 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
|
||||
#### Value Types
|
||||
|
||||
* BOOL = 'true' | 'false'
|
||||
* INT = [0,MAX_INT]
|
||||
* INT = [MIN_INT,MAX_INT]
|
||||
* UINT = [0,MAX_INT]
|
||||
* SIZE = 'NNM'; NN = INT, M = 'K' | 'M' | 'G' | 'T'
|
||||
* STR = string
|
||||
* FUNC = FUSE function
|
||||
* CATEGORY = FUSE function category
|
||||
* FUNC = filesystem function
|
||||
* CATEGORY = function category
|
||||
* POLICY = mergerfs function policy
|
||||
|
||||
|
||||
### branches
|
||||
|
||||
The 'branches' (formerly 'srcmounts') argument is a colon (':') delimited list of paths to be pooled together. It does not matter if the paths are on the same or different drives nor does it matter the filesystem. Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
|
||||
The 'branches' argument is a colon (':') delimited list of paths to be pooled together. It does not matter if the paths are on the same or different drives nor does it matter the filesystem (within reason). Used and available space will not be duplicated for paths on the same device and any features which aren't supported by the underlying filesystem (such as file attributes or extended attributes) will return the appropriate errors.
|
||||
|
||||
Branches currently have two options which can be set. A type which impacts whether or not the branch is included in a policy calculation and a individual minfreespace value. The values are set by prepending an `=` at the end of a branch designation and using commas as delimiters. Example: /mnt/drive=RW,1234
|
||||
|
||||
|
||||
#### branch type
|
||||
|
||||
* RW: (read/write) - Default behavior. Will be eligible in all policy categories.
|
||||
* RO: (read-only) - Will be excluded from `create` and `action` policies. Same as a read-only mounted filesystem would be (though faster to process).
|
||||
* NC: (no-create) - Will be excluded from `create` policies. You can't create on that branch but you can change or delete.
|
||||
|
||||
|
||||
#### minfreespace
|
||||
|
||||
Same purpose as the global option but specific to the branch. If not set the global value is used.
|
||||
|
||||
|
||||
#### globbing
|
||||
|
||||
To make it easier to include multiple branches mergerfs supports [globbing](http://linux.die.net/man/7/glob). **The globbing tokens MUST be escaped when using via the shell else the shell itself will apply the glob itself.**
|
||||
|
||||
Each branch can have a suffix of `=RW` (read / write), `=RO` (read-only), or `=NC` (no create). These suffixes work with globs as well and will apply to each path found. `RW` is the default behavior and those paths will be eligible for all policy categories. `RO` will exclude those paths from `create` and `action` policies (just as a filesystem being mounted `ro` would). `NC` will exclude those paths from `create` policies (you can't create but you can change / delete).
|
||||
|
||||
```
|
||||
# mergerfs -o allow_other,use_ino /mnt/disk\*:/mnt/cdrom /media/drives
|
||||
@@ -168,7 +193,7 @@ To have the pool mounted at boot or otherwise accessible from related tools use
|
||||
/mnt/disk*:/mnt/cdrom /mnt/pool fuse.mergerfs allow_other,use_ino 0 0
|
||||
```
|
||||
|
||||
**NOTE:** the globbing is done at mount or xattr update time (see below). If a new directory is added matching the glob after the fact it will not be automatically included.
|
||||
**NOTE:** the globbing is done at mount or when updated using the runtime API. If a new directory is added matching the glob after the fact it will not be automatically included.
|
||||
|
||||
**NOTE:** for mounting via **fstab** to work you must have **mount.fuse** installed. For Ubuntu/Debian it is included in the **fuse** package.
|
||||
|
||||
@@ -218,6 +243,54 @@ In Linux 4.20 a new feature was added allowing the negotiation of the max messag
|
||||
Since there should be no downsides to increasing `fuse_msg_size` / `max_pages`, outside a minor bump in RAM usage due to larger message buffers, mergerfs defaults the value to 256. On kernels before 4.20 the value has no effect. The reason the value is configurable is to enable experimentation and benchmarking. See the BENCHMARKING section for examples.
|
||||
|
||||
|
||||
### follow-symlinks
|
||||
|
||||
This feature, when enabled, will cause symlinks to be interpreted by mergerfs as their target (depending on the mode).
|
||||
|
||||
When there is a getattr/stat request for a file mergerfs will check if the file is a symlink and depending on the `follow-symlinks` setting will replace the information about the symlink with that of that which it points to.
|
||||
|
||||
When unlink'ing or rmdir'ing the followed symlink it will remove the symlink itself and not that which it points to.
|
||||
|
||||
* never: Behave as normal. Symlinks are treated as such.
|
||||
* directory: Resolve symlinks only which point to directories.
|
||||
* regular: Resolve symlinks only which point to regular files.
|
||||
* all: Resolve all symlinks to that which they point to.
|
||||
|
||||
Symlinks which do not point to anything are left as is.
|
||||
|
||||
WARNING: This feature works but there might be edge cases yet found. If you find any odd behaviors please file a ticket on [github](https://github.com/trapexit/mergerfs/issues).
|
||||
|
||||
|
||||
### link-exdev
|
||||
|
||||
If using path preservation and a `link` fails with EXDEV make a call to `symlink` where the `target` is the `oldlink` and the `linkpath` is the `newpath`. The `target` value is determined by the value of `link-exdev`.
|
||||
|
||||
* passthrough: Return EXDEV as normal.
|
||||
* rel-symlink: A relative path from the `newpath`.
|
||||
* abs-base-symlink: A absolute value using the underlying branch.
|
||||
* abs-pool-symlink: A absolute value using the mergerfs mount point.
|
||||
|
||||
NOTE: It is possible that some applications check the file they link. In those cases it is possible it will error or complain.
|
||||
|
||||
|
||||
### rename-exdev
|
||||
|
||||
If using path preservation and a `rename` fails with EXDEV:
|
||||
|
||||
1. Move file from **/branch/a/b/c** to **/branch/.mergerfs_rename_exdev/a/b/c**.
|
||||
2. symlink the rename's `newpath` to the moved file.
|
||||
|
||||
The `target` value is determined by the value of `rename-exdev`.
|
||||
|
||||
* passthrough: Return EXDEV as normal.
|
||||
* rel-symlink: A relative path from the `newpath`.
|
||||
* abs-symlink: A absolute value using the mergerfs mount point.
|
||||
|
||||
NOTE: It is possible that some applications check the file they rename. In those cases it is possible it will error or complain.
|
||||
|
||||
NOTE: The reason `abs-symlink` is not split into two like `link-exdev` is due to the complexities in managing absolute base symlinks when multiple `oldpaths` exist.
|
||||
|
||||
|
||||
### symlinkify
|
||||
|
||||
Due to the levels of indirection introduced by mergerfs and the underlying technology FUSE there can be varying levels of performance degradation. This feature will turn non-directories which are not writable into symlinks to the original file found by the `readlink` policy after the mtime and ctime are older than the timeout.
|
||||
@@ -251,28 +324,50 @@ NFS is not fully POSIX compliant and historically certain behaviors, such as ope
|
||||
|
||||
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 stituation 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.
|
||||
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 applying anytime a readonly file which is empty is opened for writing.
|
||||
|
||||
|
||||
# FUNCTIONS / POLICIES / CATEGORIES
|
||||
# FUNCTIONS, CATEGORIES and POLICIES
|
||||
|
||||
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. For ease of configuration in mergerfs most of the core functions are grouped into 3 categories: **action**, **create**, and **search**. These functions and categories can be assigned a policy which dictates which underlying branch/file/directory is chosen when performing that behavior. Any policy can be assigned to a function or category though some may not be very useful in practice. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` if there were more than one copy of the file.
|
||||
The POSIX filesystem API is made up of a number of functions. **creat**, **stat**, **chown**, etc. For ease of configuration in mergerfs most of the core functions are grouped into 3 categories: **action**, **create**, and **search**. These functions and categories can be assigned a policy which dictates which branch is chosen when performing that function.
|
||||
|
||||
Some functions, listed in the category `N/A` below, can not be assigned the normal policies. All functions which work on file handles use the handle which was acquired by `open` or `create`. `readdir` has no real need for a policy given the purpose is merely to return a list of entries in a directory. `statfs`'s behavior can be modified via other options. That said many times the current FUSE kernel driver will not always provide the file handle when a client calls `fgetattr`, `fchown`, `fchmod`, `futimens`, `ftruncate`, etc. This means it will call the regular, path based, versions.
|
||||
Some functions, listed in the category `N/A` below, can not be assigned the normal policies. These functions work with file handles, rather than file paths, which were created by `open` or `create`. That said many times the current FUSE kernel driver will not always provide the file handle when a client calls `fgetattr`, `fchown`, `fchmod`, `futimens`, `ftruncate`, etc. This means it will call the regular, path based, versions. `readdir` has no real need for a policy given the purpose is merely to return a list of entries in a directory. `statfs`'s behavior can be modified via other options.
|
||||
|
||||
When using policies which are based on a branch's available space the base path provided is used. Not the full path to the file in question. Meaning that sub mounts won't be considered in the space calculations. The reason is that it doesn't really work for non-path preserving policies and can lead to non-obvious behaviors.
|
||||
When using policies which are based on a branch's available space the base path provided is used. Not the full path to the file in question. Meaning that mounts in the branch won't be considered in the space calculations. The reason is that it doesn't really work for non-path preserving policies and can lead to non-obvious behaviors.
|
||||
|
||||
NOTE: While any policy can be assigned to a function or category though some may not be very useful in practice. For instance: **rand** (random) may be useful for file creation (create) but could lead to very odd behavior if used for `chmod` if there were more than one copy of the file.
|
||||
|
||||
|
||||
#### Functions and their Category classifications
|
||||
### Functions and their Category classifications
|
||||
|
||||
| Category | FUSE Functions |
|
||||
|----------|-------------------------------------------------------------------------------------|
|
||||
| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens |
|
||||
| create | create, mkdir, mknod, symlink |
|
||||
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
|
||||
| search | access, getattr, getxattr, ioctl (directories), listxattr, open, readlink |
|
||||
| N/A | fchmod, fchown, futimens, ftruncate, fallocate, fgetattr, fsync, ioctl (files), read, readdir, release, statfs, write, copy_file_range |
|
||||
|
||||
In cases where something may be searched (to confirm a directory exists across all source mounts) **getattr** will be used.
|
||||
In cases where something may be searched for (such as a path to clone) **getattr** will usually be used.
|
||||
|
||||
|
||||
### Policies
|
||||
|
||||
A policy is the algorithm used to choose a branch or branches for a function to work on. Think of them as ways to filter and sort branches.
|
||||
|
||||
Any function in the `create` category will clone the relative path if needed. Some other functions (`rename`,`link`,`ioctl`) have special requirements or behaviors which you can read more about below.
|
||||
|
||||
|
||||
#### Filtering
|
||||
|
||||
Policies basically search branches and create a list of files / paths for functions to work on. The policy is responsible for filtering and sorting the branches. Filters include **minfreespace**, whether or not a branch is mounted read-only, and the branch tagging (RO,NC,RW). These filters are applied across all policies unless otherwise noted.
|
||||
|
||||
* No **search** function policies filter.
|
||||
* All **action** function policies filter out branches which are mounted **read-only** or tagged as **RO (read-only)**.
|
||||
* All **create** function policies filter out branches which are mounted **read-only**, tagged **RO (read-only)** or **NC (no create)**, or has available space less than `minfreespace`.
|
||||
|
||||
Policies may have their own additional filtering such as those that require existing paths to be present.
|
||||
|
||||
If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch. **ENOENT** will be returned if no eligible branch is found.
|
||||
|
||||
|
||||
#### Path Preservation
|
||||
@@ -288,40 +383,32 @@ When using non-path preserving policies paths will be cloned to target drives as
|
||||
With the `msp` or `most shared path` policies they are defined as `path preserving` for the purpose of controlling `link` and `rename`'s behaviors since `ignorepponrename` is available to disable that behavior. In mergerfs v3.0 the path preserving behavior of rename and link will likely be separated from the policy all together.
|
||||
|
||||
|
||||
#### Filters
|
||||
|
||||
Policies basically search branches and create a list of files / paths for functions to work on. The policy is responsible for filtering and sorting. Filters include **minfreespace**, whether or not a branch is mounted read-only, and the branch tagging (RO,NC,RW). The policy defines the sorting but filtering is mostly uniform as described below.
|
||||
|
||||
* No **search** policies filter.
|
||||
* All **action** policies will filter out branches which are mounted **read-only** or tagged as **RO (read-only)**.
|
||||
* All **create** policies will filter out branches which are mounted **read-only**, tagged **RO (read-only)** or **NC (no create)**, or has available space less than `minfreespace`.
|
||||
|
||||
If all branches are filtered an error will be returned. Typically **EROFS** (read-only filesystem) or **ENOSPC** (no space left on device) depending on the most recent reason for filtering a branch.
|
||||
|
||||
|
||||
#### Policy descriptions
|
||||
|
||||
Because of the nature of the behavior the policies act diffierently depending on the function it is used with (based on the category).
|
||||
A policy's behavior differs, as mentioned above, based on the function it is used with. Sometimes it really might not make sense to even offer certain policies because they are literally the same as others but it makes things a bit more uniform. In mergerfs 3.0 this might change.
|
||||
|
||||
|
||||
| Policy | Description |
|
||||
|------------------|------------------------------------------------------------|
|
||||
| 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). |
|
||||
| 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: 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 **epmfss**. Action: same as **epmfs**. Create: like **eplmfs** but walk back the path if it fails to find a branch at that level. |
|
||||
| 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.
|
||||
@@ -341,19 +428,6 @@ Because of the nature of the behavior the policies act diffierently depending on
|
||||
When `ioctl` is used with an open file then it will use the file handle which was created at the original `open` call. However, when using `ioctl` with a directory mergerfs will use the `open` policy to find the directory to act on.
|
||||
|
||||
|
||||
#### unlink
|
||||
|
||||
In FUSE there is an opaque "file handle" which is created by `open`, `create`, or `opendir`, passed to the kernel, and then is passed back to the FUSE userland application by the kernel. Unfortunately, the FUSE kernel driver does not always send the file handle when it theoretically could/should. This complicates certain behaviors / workflows particularly in the high level API. As a result mergerfs is currently doing a few hacky things.
|
||||
|
||||
libfuse2 and libfuse3, when using the high level API, will rename names to `.fuse_hiddenXXXXXX` if the file is open when unlinked or renamed over. It does this so the file is still available when a request referencing the now missing file is made. This file however keeps a `rmdir` from succeeding and can be picked up by software reading directories.
|
||||
|
||||
The change mergerfs has done is that if a file is open when an unlink or rename happens it will open the file and keep it open till closed by all those who opened it prior. When a request comes in referencing that file and it doesn't include a file handle it will instead use the file handle created at unlink/rename time.
|
||||
|
||||
This won't result in technically proper behavior but close enough for many usecases.
|
||||
|
||||
The plan is to rewrite mergerfs to use the low level API so these invasive libfuse changes are no longer necessary.
|
||||
|
||||
|
||||
#### rename & link ####
|
||||
|
||||
**NOTE:** If you're receiving errors from software when files are moved / renamed / linked then you should consider changing the create policy to one which is **not** path preserving, enabling `ignorepponrename`, or contacting the author of the offending software and requesting that `EXDEV` (cross device / improper link) be properly handled.
|
||||
@@ -407,9 +481,39 @@ The above behavior will help minimize the likelihood of EXDEV being returned but
|
||||
The options `statfs` and `statfs_ignore` can be used to modify `statfs` behavior.
|
||||
|
||||
|
||||
# ERROR HANDLING
|
||||
|
||||
POSIX filesystem functions offer a single return code meaning that there is some complication regarding the handling of multiple branches as mergerfs does. It tries to handle errors in a way that would generally return meaningful values for that particular function.
|
||||
|
||||
### chmod, chown, removexattr, setxattr, truncate, utimens
|
||||
|
||||
1) if no error: return 0 (success)
|
||||
2) if no successes: return first error
|
||||
3) if one of the files acted on was the same as the related search function: return its value
|
||||
4) return 0 (success)
|
||||
|
||||
While doing this increases the complexity and cost of error handling, particularly step 3, this provides probably the most reasonable return value.
|
||||
|
||||
|
||||
### unlink, rmdir
|
||||
|
||||
1) if no errors: return 0 (success)
|
||||
2) return first error
|
||||
|
||||
Older version of mergerfs would return success if any success occurred but for unlink and rmdir there are downstream assumptions that, while not impossible to occur, can confuse some software.
|
||||
|
||||
|
||||
### others
|
||||
|
||||
For search functions there is always a single thing acted on and as such whatever return value that comes from the single function call is returned.
|
||||
|
||||
For create functions `mkdir`, `mknod`, and `symlink` which don't return a file descriptor and therefore can have `all` or `epall` policies it will return success if any of the calls succeed and an error otherwise.
|
||||
|
||||
|
||||
# BUILD / UPDATE
|
||||
|
||||
**NOTE:** Prebuilt packages can be found at: 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).
|
||||
|
||||
@@ -424,7 +528,7 @@ $ wget https://github.com/trapexit/mergerfs/releases/download/<ver>/mergerfs-<ve
|
||||
$ cd mergerfs
|
||||
$ sudo tools/install-build-pkgs
|
||||
$ make deb
|
||||
$ sudo dpkg -i ../mergerfs_version_arch.deb
|
||||
$ sudo dpkg -i ../mergerfs_<version>_<arch>.deb
|
||||
```
|
||||
|
||||
#### RHEL / CentOS /Fedora
|
||||
@@ -458,43 +562,26 @@ make LTO=1 - build with link time optimization
|
||||
```
|
||||
|
||||
|
||||
# UPGRADE
|
||||
|
||||
mergerfs can be upgraded live by mounting on top of the previous instance. Simply install the new version of mergerfs and follow the instructions below.
|
||||
|
||||
Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
|
||||
|
||||
```
|
||||
$ sudo mount /mnt/mergerfs
|
||||
$ mount | grep mergerfs
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
```
|
||||
|
||||
A problem with this approach is that the underlying instance will continue to run even if the software using it stop or are restarted. To work around this you can use a "lazy umount". Before mounting over top the mount point with the new instance of mergerfs issue: `umount -l <mergerfs_mountpoint>`.
|
||||
|
||||
|
||||
|
||||
# RUNTIME CONFIG
|
||||
|
||||
#### ioctl
|
||||
|
||||
The original runtime config API was via xattr calls. This however became an issue when needing to disable xattr. While slightly less convenient ioctl does not have the same problems and will be the main API going forward.
|
||||
|
||||
The keys are the same as the command line option arguments as well as the config file.
|
||||
|
||||
##### requests / commands
|
||||
|
||||
All commands take a 4096 byte char buffer.
|
||||
|
||||
* read keys: get a nul '\0' delimited list of option keys
|
||||
* _IOWR(0xDF,0,char[4096]) = 0xD000DF00
|
||||
* on success ioctl return value is the total length
|
||||
* read value: get an option value
|
||||
* _IOWR(0xDF,1,char[4096]) = 0xD000DF01
|
||||
* the key is passed in via the char buffer as a nul '\0' terminated string
|
||||
* on success ioctl return value is the total length
|
||||
* write value: set an option value
|
||||
* _IOW(0xDF,2,char[4096]) = 0x5000DF02
|
||||
* the key and value is passed in via the char buffer as a nul '\0' terminated string in the format of `key=value`
|
||||
* on success ioctl return value is 0
|
||||
* file info: get mergerfs metadata info for a file
|
||||
* _IOWR(0xDF,3,char[4096]) = 0xD000DF03
|
||||
* the key is passed in via the char buffer as a nul '\0' terminated string
|
||||
* on success the ioctl return value is the total length
|
||||
* keys:
|
||||
* basepath: the base mount point for the file according to the getattr policy
|
||||
* relpath: the relative path of the file from the mount point
|
||||
* fullpath: the full path of the underlying file according to the getattr policy
|
||||
* allpaths: a NUL '\0' delimited list of full paths to all files found
|
||||
|
||||
|
||||
#### .mergerfs pseudo file (deprecated) ####
|
||||
|
||||
NOTE: this interface will be removed in mergerfs 3.0
|
||||
#### .mergerfs pseudo file ####
|
||||
|
||||
```
|
||||
<mountpoint>/.mergerfs
|
||||
@@ -507,7 +594,7 @@ Any changes made at runtime are **not** persisted. If you wish for values to per
|
||||
|
||||
##### Keys #####
|
||||
|
||||
Use `xattr -l /mountpoint/.mergerfs` to see all supported keys. Some are informational and therefore read-only. `setxattr` will return EINVAL (invalid argument) on read-only keys.
|
||||
Use `getfattr -d /mountpoint/.mergerfs` or `xattr -l /mountpoint/.mergerfs` to see all supported keys. Some are informational and therefore read-only. `setxattr` will return EINVAL (invalid argument) on read-only keys.
|
||||
|
||||
|
||||
##### Values #####
|
||||
@@ -517,8 +604,6 @@ Same as the command line.
|
||||
|
||||
###### user.mergerfs.branches ######
|
||||
|
||||
**NOTE:** formerly `user.mergerfs.srcmounts` but said key is still supported.
|
||||
|
||||
Used to query or modify the list of branches. When modifying there are several shortcuts to easy manipulation of the list.
|
||||
|
||||
| Value | Description |
|
||||
@@ -538,70 +623,30 @@ The `=NC`, `=RO`, `=RW` syntax works just as on the command line.
|
||||
##### Example #####
|
||||
|
||||
```
|
||||
[trapexit:/mnt/mergerfs] $ xattr -l .mergerfs
|
||||
user.mergerfs.branches: /mnt/a=RW:/mnt/b=RW
|
||||
user.mergerfs.minfreespace: 4294967295
|
||||
user.mergerfs.moveonenospc: false
|
||||
[trapexit:/mnt/mergerfs] $ getfattr -d .mergerfs
|
||||
user.mergerfs.branches="/mnt/a=RW:/mnt/b=RW"
|
||||
user.mergerfs.minfreespace="4294967295"
|
||||
user.mergerfs.moveonenospc="false"
|
||||
...
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
|
||||
ff
|
||||
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
|
||||
user.mergerfs.category.search="ff"
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.category.search newest .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.category.search .mergerfs
|
||||
newest
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches +/mnt/c .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
|
||||
/mnt/a:/mnt/b:/mnt/c
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches =/mnt/c .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
|
||||
/mnt/c
|
||||
|
||||
[trapexit:/mnt/mergerfs] $ xattr -w user.mergerfs.branches '+</mnt/a:/mnt/b' .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.branches .mergerfs
|
||||
/mnt/a:/mnt/b:/mnt/c
|
||||
[trapexit:/mnt/mergerfs] $ setfattr -n user.mergerfs.category.search -v newest .mergerfs
|
||||
[trapexit:/mnt/mergerfs] $ getfattr -n user.mergerfs.category.search .mergerfs
|
||||
user.mergerfs.category.search="newest"
|
||||
```
|
||||
|
||||
|
||||
#### file / directory xattrs ####
|
||||
|
||||
While they won't show up when using [listxattr](http://linux.die.net/man/2/listxattr) **mergerfs** offers a number of special xattrs to query information about the files served. To access the values you will need to issue a [getxattr](http://linux.die.net/man/2/getxattr) for one of the following:
|
||||
While they won't show up when using `getfattr` **mergerfs** offers a number of special xattrs to query information about the files served. To access the values you will need to issue a [getxattr](http://linux.die.net/man/2/getxattr) for one of the following:
|
||||
|
||||
* **user.mergerfs.basepath**: the base mount point for the file given the current getattr policy
|
||||
* **user.mergerfs.relpath**: the relative path of the file from the perspective of the mount point
|
||||
* **user.mergerfs.fullpath**: the full path of the original file given the getattr policy
|
||||
* **user.mergerfs.allpaths**: a NUL ('\0') separated list of full paths to all files found
|
||||
|
||||
```
|
||||
[trapexit:/mnt/mergerfs] $ ls
|
||||
A B C
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.fullpath A
|
||||
/mnt/a/full/path/to/A
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.basepath A
|
||||
/mnt/a
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.relpath A
|
||||
/full/path/to/A
|
||||
[trapexit:/mnt/mergerfs] $ xattr -p user.mergerfs.allpaths A | tr '\0' '\n'
|
||||
/mnt/a/full/path/to/A
|
||||
/mnt/b/full/path/to/A
|
||||
```
|
||||
|
||||
|
||||
# UPGRADE
|
||||
|
||||
mergerfs can be upgraded live by mounting on top of the previous version. Simply install the new version of mergerfs and follow the instructions below.
|
||||
|
||||
Add `nonempty` to your mergerfs option list and call mergerfs again or if using `/etc/fstab` call for it to mount again. Existing open files and such will continue to work fine though they won't see runtime changes since any such change would be the new mount. If you plan on changing settings with the new mount you should / could apply those before mounting the new version.
|
||||
|
||||
```
|
||||
$ sudo mount /mnt/mergerfs
|
||||
$ mount | grep mergerfs
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
|
||||
```
|
||||
|
||||
|
||||
# TOOLING
|
||||
|
||||
@@ -612,7 +657,6 @@ media on /mnt/mergerfs type fuse.mergerfs (rw,relatime,user_id=0,group_id=0,defa
|
||||
* mergerfs.dup: Ensure there are at least N copies of a file across the pool
|
||||
* mergerfs.balance: Rebalance files across drives by moving them from the most filled to the least filled
|
||||
* mergerfs.consolidate: move files within a single mergerfs directory to the drive with most free space
|
||||
* mergerfs.mktrash: Creates FreeDesktop.org Trash specification compatible directories on a mergerfs mount
|
||||
* https://github.com/trapexit/scorch
|
||||
* scorch: A tool to help discover silent corruption of files and keep track of files
|
||||
* https://github.com/trapexit/bbf
|
||||
@@ -655,7 +699,7 @@ Given the relatively high cost of FUSE due to the kernel <-> userspace round tri
|
||||
|
||||
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 wrtieback 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.
|
||||
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).
|
||||
|
||||
@@ -695,7 +739,7 @@ MergerFS does not natively support any sort of tiered caching. Most users have n
|
||||
1. Fast network, slow drives, many readers: You've a 10+Gbps network with many readers and your regular drives can't keep up.
|
||||
2. Fast network, slow drives, small'ish bursty writes: You have a 10+Gbps network and wish to transfer amounts of data less than your cache drive but wish to do so quickly.
|
||||
|
||||
With #1 its arguable if you should be using mergerfs at all. RAID would probably be the better solution. If you're going to use mergerfs there are other tactics that may help: spreading the data across drives (see the mergerfs.dup tool) and setting `func.open=rand`, using `symlinkify`, or using dm-cache or a similar technology to add tiered cache to the underlying device.
|
||||
With #1 it's arguable if you should be using mergerfs at all. RAID would probably be the better solution. If you're going to use mergerfs there are other tactics that may help: spreading the data across drives (see the mergerfs.dup tool) and setting `func.open=rand`, using `symlinkify`, or using dm-cache or a similar technology to add tiered cache to the underlying device.
|
||||
|
||||
With #2 one could use dm-cache as well but there is another solution which requires only mergerfs and a cronjob.
|
||||
|
||||
@@ -712,6 +756,9 @@ With #2 one could use dm-cache as well but there is another solution which requi
|
||||
|
||||
Move files from cache to backing pool based only on the last time the file was accessed. Replace `-atime` with `-amin` if you want minutes rather than days. May want to use the `fadvise` / `--drop-cache` version of rsync or run rsync with the tool "nocache".
|
||||
|
||||
*NOTE:* The arguments to these scripts include the cache **drive**. Not the pool with the cache drive. You could have data loss if the source is the cache pool.
|
||||
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
@@ -733,6 +780,8 @@ find "${CACHE}" -type f -atime +${N} -printf '%P\n' | \
|
||||
|
||||
Move the oldest file from the cache to the backing pool. Continue till below percentage threshold.
|
||||
|
||||
*NOTE:* The arguments to these scripts include the cache **drive**. Not the pool with the cache drive. You could have data loss if the source is the cache pool.
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
@@ -753,7 +802,7 @@ do
|
||||
head -n 1 | \
|
||||
cut -d' ' -f2-)
|
||||
test -n "${FILE}"
|
||||
rsync -axqHAXWES --preallocate --remove-source-files "${CACHE}/./${FILE}" "${BACKING}/"
|
||||
rsync -axqHAXWESR --preallocate --remove-source-files "${CACHE}/./${FILE}" "${BACKING}/"
|
||||
done
|
||||
```
|
||||
|
||||
@@ -780,6 +829,8 @@ NOTE: be sure to read about these features before changing them to understand wh
|
||||
* use `symlinkify` if your data is largely static and read-only
|
||||
* use tiered cache drives
|
||||
* use LVM and LVM cache to place a SSD in front of your HDDs
|
||||
* if `cache.files` is enabled increase readahead: `echo "1024" > /sys/class/bdi/0:$(stat -c%d /MOUNT)/read_ahead_kb`
|
||||
* increase readahead on all devices: `ls -1 /sys/class/bdi/*/read_ahead_kb | xargs -n1 -I{} sh -c "echo 1024 > {}"`
|
||||
|
||||
If you come across a setting that significantly impacts performance please contact trapexit so he may investigate further.
|
||||
|
||||
@@ -814,16 +865,26 @@ $ dd if=/dev/zero of=/mnt/mergerfs/1GB.file bs=1M count=1024 oflag=nocache conv=
|
||||
$ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=fdatasync status=progress
|
||||
```
|
||||
|
||||
### other benchmarks
|
||||
|
||||
If you are attempting to benchmark other behaviors you must ensure you clear kernel caches before runs. In fact it would be a good deal to run before the read and write benchmarks as well just in case.
|
||||
|
||||
```
|
||||
sync
|
||||
echo 3 | sudo tee /proc/sys/vm/drop_caches
|
||||
```
|
||||
|
||||
|
||||
# TIPS / NOTES
|
||||
|
||||
* This document is very literal and thorough. Unless there is a bug things work as described. If a suspected feature isn't mentioned it doesn't exist. If certain libfuse arguments aren't listed they probably shouldn't be used.
|
||||
* Ensure you're using the latest version. Few distros have the latest version.
|
||||
* **use_ino** will only work when used with mergerfs 2.18.0 and above.
|
||||
* Run mergerfs as `root` (with **allow_other**) unless you're merging paths which are owned by the same user otherwise strange permission issues may arise.
|
||||
* https://github.com/trapexit/backup-and-recovery-howtos : A set of guides / howtos on creating a data storage system, backing it up, maintaining it, and recovering from failure.
|
||||
* If you don't see some directories and files you expect in a merged point or policies seem to skip drives be sure the user has permission to all the underlying directories. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
|
||||
* Run mergerfs as `root` (with **allow_other**) unless you're merging paths which are owned exclusively and fully by the same user otherwise strange permission issues may arise. mergerfs is designed and intended to be run as `root`.
|
||||
* If you don't see some directories and files you expect, policies seem to skip branches, you get strange permission errors, etc. be sure the underlying filesystems' permissions are all the same. Use `mergerfs.fsck` to audit the drive for out of sync permissions.
|
||||
* If you still have permission issues be sure you are using POSIX ACL compliant filesystems. mergerfs doesn't generally make exceptions for FAT, NTFS, or other non-POSIX filesystem.
|
||||
* Do **not** use `cache.files=off` if you expect applications (such as rtorrent) to use [mmap](http://linux.die.net/man/2/mmap) files. Shared mmap is not currently supported in FUSE w/ page caching disabled. Enabling `dropcacheonclose` is recommended when `cache.files=partial|full|auto-full`.
|
||||
* Since POSIX functions give only a singular error or success its difficult to determine the proper behavior when applying the function to multiple targets. **mergerfs** will return an error only if all attempts of an action fail. Any success will lead to a success returned. This means however that some odd situations may arise.
|
||||
* [Kodi](http://kodi.tv), [Plex](http://plex.tv), [Subsonic](http://subsonic.org), etc. can use directory [mtime](http://linux.die.net/man/2/stat) to more efficiently determine whether to scan for new content rather than simply performing a full scan. If using the default **getattr** policy of **ff** it's possible those programs will miss an update on account of it returning the first directory found's **stat** info and its a later directory on another mount which had the **mtime** recently updated. To fix this you will want to set **func.getattr=newest**. Remember though that this is just **stat**. If the file is later **open**'ed or **unlink**'ed and the policy is different for those then a completely different file or directory could be acted on.
|
||||
* [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 it's 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.
|
||||
|
||||
@@ -834,6 +895,7 @@ $ dd if=/mnt/mergerfs/1GB.file of=/dev/null bs=1M count=1024 iflag=nocache conv=
|
||||
|
||||
[https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs](https://github.com/trapexit/mergerfs/wiki/Kernel-Issues-&-Bugs)
|
||||
|
||||
|
||||
#### directory mtime is not being updated
|
||||
|
||||
Remember that the default policy for `getattr` is `ff`. The information for the first directory found will be returned. If it wasn't the directory which had been updated then it will appear outdated.
|
||||
@@ -1010,17 +1072,35 @@ The default create policy is `epmfs`. That is a path preserving algorithm. With
|
||||
This catches a lot of new users off guard but changing the default would break the setup for many existing users. If you do not care about path preservation and wish your files to be spread across all your drives change to `mfs` or similar policy as described above. If you do want path preservation you'll need to perform the manual act of creating paths on the drives you want the data to land on before transferring your data. Setting `func.mkdir=epall` can simplify managing path preservation for `create`. Or use `func.mkdir=rand` if you're interested in just grouping together directory content by drive.
|
||||
|
||||
|
||||
#### Do hard links work?
|
||||
#### Do hardlinks work?
|
||||
|
||||
Yes. You need to use `use_ino` to support proper reporting of inodes but they work regardless. See also the option `inodecalc`.
|
||||
|
||||
What mergerfs does not do is fake hard links across branches. Read the section "rename & link" for how it works.
|
||||
|
||||
Remember that hardlinks will NOT work across devices. That includes between the original filesystem and a mergerfs pool, between two separate pools of the same underlying filesystems, or bind mounts of paths within the mergerfs pool. The latter is common when using Docker or Podman. Multiple volumes (bind mounts) to the same underlying filesystem are considered different devices. There is no way to link between them. You should mount in the highest directory in the mergerfs pool that includes all the paths you need if you want links to work.
|
||||
|
||||
#### Does mergerfs support CoW / copy-on-write?
|
||||
|
||||
#### Can I use mergerfs without SnapRAID? SnapRAID without mergerfs?
|
||||
|
||||
Yes. They are completely unrelated pieces of software.
|
||||
|
||||
|
||||
#### Can mergerfs run via Docker, Podman, Kubernetes, etc.
|
||||
|
||||
Yes. With Docker you'll need to include `--cap-add=SYS_ADMIN --device=/dev/fuse --security-opt=apparmor:unconfined` or similar with other container runtimes. You should also be running it as root or given sufficient caps to change user and group identity as well as have root like filesystem permissions.
|
||||
|
||||
Keep in mind that you **MUST** consider identity when using containers. For example: supplemental groups will be picked up from the container unless you properly manage users and groups by sharing relevant /etc files or by using some other means to share identity across containers. Similarly if you use "rootless" containers and user namespaces to do uid/gid translations you **MUST** consider that while managing shared files.
|
||||
|
||||
Also, as mentioned by [hotio](https://hotio.dev/containers/mergerfs), with Docker you should probably be mounting with `bind-propagation` set to `slave`.
|
||||
|
||||
|
||||
#### Does mergerfs support CoW / copy-on-write / writes to read-only filesystems?
|
||||
|
||||
Not in the sense of a filesystem like BTRFS or ZFS nor in the overlayfs or aufs sense. It does offer a [cow-shell](http://manpages.ubuntu.com/manpages/bionic/man1/cow-shell.1.html) like hard link breaking (copy to temp file then rename over original) which can be useful when wanting to save space by hardlinking duplicate files but wish to treat each name as if it were a unique and separate file.
|
||||
|
||||
If you want to write to a read-only filesystem you should look at overlayfs. You can always include the overlayfs mount into a mergerfs pool.
|
||||
|
||||
|
||||
#### Why can't I see my files / directories?
|
||||
|
||||
@@ -1031,6 +1111,32 @@ Whenever you run into a split permission issue (seeing some but not all files) t
|
||||
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 use FUSE? Why not a kernel based solution?
|
||||
|
||||
As with any two solutions to a problem there are advantages and disadvantages to each one.
|
||||
|
||||
A FUSE based solution has all the downsides of FUSE:
|
||||
|
||||
* Higher IO latency due to the trips in and out of kernel space
|
||||
* Higher general overhead due to trips in and out of kernel space
|
||||
* Double caching when using page caching
|
||||
* Misc limitations due to FUSE's design
|
||||
|
||||
But FUSE also has a lot of upsides:
|
||||
|
||||
* Easier to offer a cross platform solution
|
||||
* Easier forward and backward compatibility
|
||||
* Easier updates for users
|
||||
* Easier and faster release cadence
|
||||
* Allows more flexibility in design and features
|
||||
* Overall easier to write, secure, and maintain
|
||||
* Ability to run without root access or need to change the kernel
|
||||
* Much lower barrier to entry (getting code into the kernel takes a lot of time and effort initially)
|
||||
|
||||
|
||||
FUSE was chosen because of all the advantages listed above. The negatives of FUSE do not outweigh the positives.
|
||||
|
||||
|
||||
#### Is my OS's libfuse needed for mergerfs to work?
|
||||
|
||||
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`.
|
||||
@@ -1048,7 +1154,7 @@ If `mergerfs` doesn't work as a type it could be due to how the `mount.mergerfs`
|
||||
|
||||
See above first.
|
||||
|
||||
If/when mergerfs is rewritten to use the low-level API then it'll be plausible to support system libfuse but till then its simply too much work to manage the differences across the versions.
|
||||
If/when mergerfs is rewritten to use the low-level API then it'll be plausible to support system libfuse but till then it's simply too much work to manage the differences across the versions.
|
||||
|
||||
|
||||
#### Why use mergerfs over mhddfs?
|
||||
@@ -1155,7 +1261,7 @@ Yes.
|
||||
|
||||
#### I notice massive slowdowns of writes when enabling cache.files.
|
||||
|
||||
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 degregation, especially when using a network filesystem (such as NFS or CIFS/SMB/Samba.) Unfortunately at this moment the kernel is not caching the response.
|
||||
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.
|
||||
|
||||
To work around this situation mergerfs offers a few solutions.
|
||||
|
||||
@@ -1185,48 +1291,40 @@ Filesystems are complex and difficult to debug. mergerfs, while being just a pro
|
||||
|
||||
**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.**
|
||||
|
||||
**For commercial support or feature requests please contact me directly.**
|
||||
|
||||
|
||||
#### Information to include in bug reports
|
||||
|
||||
* Version of mergerfs: `mergerfs -V`
|
||||
* mergerfs settings: from `/etc/fstab` or command line execution
|
||||
* Version of Linux: `uname -a`
|
||||
* Versions of any additional software being used
|
||||
* List of drives, their filesystems, and sizes (before and after issue): `df -h`
|
||||
* **All** information about the relevant branches and paths: permissions, etc.
|
||||
* mergerfs settings / arguments: from fstab, systemd unit, command line, etc.
|
||||
* Version of the OS: `uname -a`
|
||||
* List of branches, their filesystem types, sizes (before and after issue): `df -h`
|
||||
* **All** information about the relevant branches and paths: permissions, ownership, etc.
|
||||
* **All** information about the client app making the requests: version, uid/gid
|
||||
* Runtime environment: mostly are things running inside containers or not
|
||||
* A `strace` of the app having problems:
|
||||
* `strace -f -o /tmp/app.strace.txt <cmd>`
|
||||
* `strace -fvTtt -s 256 -o /tmp/app.strace.txt <cmd>`
|
||||
* A `strace` of mergerfs while the program is trying to do whatever it's failing to do:
|
||||
* `strace -f -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
* `strace -fvTtt -s 256 -p <mergerfsPID> -o /tmp/mergerfs.strace.txt`
|
||||
* **Precise** directions on replicating the issue. Do not leave **anything** out.
|
||||
* Try to recreate the problem in the simplist way using standard programs.
|
||||
* Try to recreate the problem in the simplest way using standard programs: ln, mv, cp, ls, dd, etc.
|
||||
|
||||
|
||||
#### Contact / Issue submission
|
||||
|
||||
* github.com: https://github.com/trapexit/mergerfs/issues
|
||||
* email: trapexit@spawn.link
|
||||
* discord: https://discord.gg/MpAr69V
|
||||
* twitter: https://twitter.com/_trapexit
|
||||
* reddit: https://www.reddit.com/user/trapexit
|
||||
* discord: https://discord.gg/MpAr69V
|
||||
|
||||
|
||||
#### Support development
|
||||
|
||||
This software is free to use and released under a very liberal license. That said if you like this software and would like to support its development donations are welcome.
|
||||
This software is released under the very liberal ISC license and is therefore free to use for personal or commercial uses. That said if you like this software and have the means please consider supporting its development.
|
||||
|
||||
* PayPal: https://paypal.me/trapexit
|
||||
* GitHub Sponsors: https://github.com/sponsors/trapexit
|
||||
* Patreon: https://www.patreon.com/trapexit
|
||||
* SubscribeStar: https://www.subscribestar.com/trapexit
|
||||
* Ko-Fi: https://ko-fi.com/trapexit
|
||||
* Bitcoin (BTC): 1DfoUd2m5WCxJAMvcFuvDpT4DR2gWX2PWb
|
||||
* Bitcoin Cash (BCH): qrf257j0l09yxty4kur8dk2uma8p5vntdcpks72l8z
|
||||
* Ethereum (ETH): 0xb486C0270fF75872Fc51d85879b9c15C380E66CA
|
||||
* Litecoin (LTC): LW1rvHRPWtm2NUEMhJpP4DjHZY1FaJ1WYs
|
||||
* Basic Attention Token (BAT): 0xE651d4900B4C305284Da43E2e182e9abE149A87A
|
||||
* Zcash (ZEC): t1ZwTgmbQF23DJrzqbAmw8kXWvU2xUkkhTt
|
||||
* Zcoin (XZC): a8L5Vz35KdCQe7Y7urK2pcCGau7JsqZ5Gw
|
||||
https://github.com/trapexit/support
|
||||
|
||||
|
||||
# LINKS
|
||||
|
||||
-3547
File diff suppressed because it is too large
Load Diff
+24
-10
@@ -10,7 +10,7 @@ INSTALLUTILS :=
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
OPT_FLAGS := -O0 -g
|
||||
OPT_FLAGS := -O0 -g -fsanitize=undefined
|
||||
else
|
||||
OPT_FLAGS := -O2
|
||||
endif
|
||||
@@ -29,16 +29,18 @@ INSTALLBINDIR = $(DESTDIR)$(BINDIR)
|
||||
INSTALLSBINDIR = $(DESTDIR)$(SBINDIR)
|
||||
INSTALLMAN1DIR = $(DESTDIR)$(MAN1DIR)
|
||||
|
||||
AR ?= ar
|
||||
|
||||
SRC = \
|
||||
lib/buffer.c \
|
||||
lib/cuse_lowlevel.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/crc32b.c \
|
||||
lib/debug.c \
|
||||
lib/fuse.c \
|
||||
lib/fuse_kern_chan.c \
|
||||
lib/fuse_loop.c \
|
||||
lib/fuse_dirents.c \
|
||||
lib/fuse_loop_mt.c \
|
||||
lib/fuse_lowlevel.c \
|
||||
lib/fuse_mt.c \
|
||||
lib/fuse_node.c \
|
||||
lib/fuse_opt.c \
|
||||
lib/fuse_session.c \
|
||||
lib/fuse_signals.c \
|
||||
@@ -46,9 +48,17 @@ SRC = \
|
||||
lib/mount.c
|
||||
OBJS = $(SRC:lib/%.c=build/%.o)
|
||||
DEPS = $(SRC:lib/%.c=build/%.d)
|
||||
|
||||
CFLAGS = \
|
||||
$(OPT_FLAGS) \
|
||||
CFLAGS ?= \
|
||||
$(OPT_FLAGS)
|
||||
CFLAGS := \
|
||||
${CFLAGS} \
|
||||
-std=gnu99 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
-MMD
|
||||
CXXFLAGS := \
|
||||
${CXXFLAGS} \
|
||||
-std=c++17 \
|
||||
-Wall \
|
||||
-pipe \
|
||||
-MMD
|
||||
@@ -61,7 +71,8 @@ FUSE_FLAGS = \
|
||||
-DFUSE_USE_VERSION=29 \
|
||||
-DPACKAGE_VERSION=\"$(VERSION)\" \
|
||||
-DFUSERMOUNT_DIR=\"$(FUSERMOUNT_DIR)\"
|
||||
LDFLAGS = \
|
||||
LDFLAGS := \
|
||||
${LDFLAGS} \
|
||||
-lrt \
|
||||
-pthread
|
||||
|
||||
@@ -78,7 +89,7 @@ objects: build/config.h
|
||||
$(MAKE) $(OBJS)
|
||||
|
||||
build/libfuse.a: objects
|
||||
ar rcs build/libfuse.a $(OBJS)
|
||||
${AR} rcs build/libfuse.a $(OBJS)
|
||||
|
||||
utils: mergerfs-fusermount mount.mergerfs
|
||||
|
||||
@@ -95,6 +106,9 @@ mount.mergerfs: build/mount.mergerfs
|
||||
build/%.o: lib/%.c
|
||||
$(CC) $(CFLAGS) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
build/%.o: lib/%.cpp
|
||||
$(CXX) $(CXXFLAGS) $(FUSE_FLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <malloc.h>
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
malloc_trim(0);
|
||||
}
|
||||
@@ -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
|
||||
+465
-752
File diff suppressed because it is too large
Load Diff
+165
-207
@@ -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,24 +39,15 @@
|
||||
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
|
||||
#define FUSE_MAX_MAX_PAGES 256
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct fuse_timeouts_s fuse_timeouts_t;
|
||||
struct fuse_timeouts_s
|
||||
{
|
||||
uint64_t entry;
|
||||
uint64_t attr;
|
||||
};
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
/**
|
||||
* Information about open files
|
||||
*
|
||||
* Changed in version 2.5
|
||||
*/
|
||||
struct
|
||||
fuse_file_info
|
||||
typedef struct fuse_file_info_t fuse_file_info_t;
|
||||
struct fuse_file_info_t
|
||||
{
|
||||
/** Open flags. Available in open() and release() */
|
||||
int flags;
|
||||
@@ -158,60 +152,61 @@ 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;
|
||||
struct fuse_chan;
|
||||
struct fuse_pollhandle;
|
||||
struct fuse_pollhandle_t;
|
||||
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
|
||||
|
||||
/**
|
||||
* Create a FUSE mountpoint
|
||||
@@ -241,7 +236,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
|
||||
@@ -251,12 +245,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
|
||||
@@ -278,7 +272,7 @@ int fuse_version(void);
|
||||
*
|
||||
* @param ph the poll handle
|
||||
*/
|
||||
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph);
|
||||
void fuse_pollhandle_destroy(fuse_pollhandle_t *ph);
|
||||
|
||||
/* ----------------------------------------------------------- *
|
||||
* Data buffer *
|
||||
@@ -288,73 +282,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),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -364,36 +358,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -405,41 +399,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
|
||||
@@ -458,7 +452,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 *
|
||||
@@ -485,42 +479,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,196 +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 (*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 *, 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 (*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 *, 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);
|
||||
|
||||
struct fuse_operations_compat2 {
|
||||
int (*getattr) (const char *, struct stat *);
|
||||
int (*readlink) (const char *, char *, size_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 *, 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 (*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__ */
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
#include "kvec.h"
|
||||
#include "fuse_dirent.h"
|
||||
@@ -36,21 +36,19 @@ extern "C" {
|
||||
#include <unistd.h>
|
||||
|
||||
enum fuse_dirents_type_e
|
||||
{
|
||||
UNSET = 0,
|
||||
NORMAL,
|
||||
PLUS
|
||||
};
|
||||
{
|
||||
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
|
||||
typedef struct fuse_dirents_t fuse_dirents_t;
|
||||
struct fuse_dirents_t
|
||||
{
|
||||
char *buf;
|
||||
uint64_t buf_len;
|
||||
uint64_t data_len;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
kvec_t(char) data;
|
||||
kvec_t(uint32_t) offs;
|
||||
fuse_dirents_type_t type;
|
||||
};
|
||||
|
||||
int fuse_dirents_init(fuse_dirents_t *d);
|
||||
@@ -79,6 +77,4 @@ void *fuse_dirents_find(fuse_dirents_t *d,
|
||||
|
||||
int fuse_dirents_convert_plus2normal(fuse_dirents_t *d);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
EXTERN_C_END
|
||||
|
||||
+165
-22
@@ -172,6 +172,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
|
||||
*
|
||||
* 7.32
|
||||
* - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
|
||||
*
|
||||
* 7.33
|
||||
* - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
|
||||
* - add FUSE_OPEN_KILL_SUIDGID
|
||||
* - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
|
||||
* - add FUSE_SETXATTR_ACL_KILL_SGID
|
||||
*
|
||||
* 7.34
|
||||
* - add FUSE_SYNCFS
|
||||
*
|
||||
* 7.35
|
||||
* - add FOPEN_NOFLUSH
|
||||
*
|
||||
* 7.36
|
||||
* - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
|
||||
* - add flags2 to fuse_init_in and fuse_init_out
|
||||
* - add FUSE_SECURITY_CTX init flag
|
||||
* - add security context to create, mkdir, symlink, and mknod requests
|
||||
* - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX
|
||||
*
|
||||
* 7.37
|
||||
* - add FUSE_TMPFILE
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@@ -207,7 +232,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 31
|
||||
#define FUSE_KERNEL_MINOR_VERSION 37
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@@ -231,7 +256,7 @@ struct fuse_attr {
|
||||
uint32_t gid;
|
||||
uint32_t rdev;
|
||||
uint32_t blksize;
|
||||
uint32_t padding;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct fuse_kstatfs {
|
||||
@@ -257,17 +282,18 @@ struct fuse_file_lock {
|
||||
/**
|
||||
* Bitmasks for fuse_setattr_in.valid
|
||||
*/
|
||||
#define FATTR_MODE (1 << 0)
|
||||
#define FATTR_UID (1 << 1)
|
||||
#define FATTR_GID (1 << 2)
|
||||
#define FATTR_SIZE (1 << 3)
|
||||
#define FATTR_ATIME (1 << 4)
|
||||
#define FATTR_MTIME (1 << 5)
|
||||
#define FATTR_FH (1 << 6)
|
||||
#define FATTR_ATIME_NOW (1 << 7)
|
||||
#define FATTR_MTIME_NOW (1 << 8)
|
||||
#define FATTR_LOCKOWNER (1 << 9)
|
||||
#define FATTR_CTIME (1 << 10)
|
||||
#define FATTR_MODE (1 << 0)
|
||||
#define FATTR_UID (1 << 1)
|
||||
#define FATTR_GID (1 << 2)
|
||||
#define FATTR_SIZE (1 << 3)
|
||||
#define FATTR_ATIME (1 << 4)
|
||||
#define FATTR_MTIME (1 << 5)
|
||||
#define FATTR_FH (1 << 6)
|
||||
#define FATTR_ATIME_NOW (1 << 7)
|
||||
#define FATTR_MTIME_NOW (1 << 8)
|
||||
#define FATTR_LOCKOWNER (1 << 9)
|
||||
#define FATTR_CTIME (1 << 10)
|
||||
#define FATTR_KILL_SUIDGID (1 << 11)
|
||||
|
||||
/**
|
||||
* Flags returned by the OPEN request
|
||||
@@ -277,12 +303,14 @@ struct fuse_file_lock {
|
||||
* 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)
|
||||
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
|
||||
*/
|
||||
#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)
|
||||
#define FOPEN_NOFLUSH (1 << 5)
|
||||
|
||||
/**
|
||||
* INIT request/reply flags
|
||||
@@ -313,7 +341,21 @@ struct fuse_file_lock {
|
||||
* 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
|
||||
* FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for
|
||||
* foffset and moffset fields in struct
|
||||
* fuse_setupmapping_out and fuse_removemapping_one.
|
||||
* FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
|
||||
* FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc.
|
||||
* Upon write/truncate suid/sgid is only killed if caller
|
||||
* does not have CAP_FSETID. Additionally upon
|
||||
* write/truncate sgid is killed only if file has group
|
||||
* execute permission. (Same as Linux VFS behavior).
|
||||
* FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
|
||||
* FUSE_INIT_EXT: extended fuse_init_in request
|
||||
* FUSE_INIT_RESERVED: reserved, do not use
|
||||
* FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and
|
||||
* mknod
|
||||
* FUSE_HAS_INODE_DAX: use per inode DAX
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@@ -342,6 +384,14 @@ struct fuse_file_lock {
|
||||
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
|
||||
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
||||
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
||||
#define FUSE_SUBMOUNTS (1 << 27)
|
||||
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
|
||||
#define FUSE_SETXATTR_EXT (1 << 29)
|
||||
#define FUSE_INIT_EXT (1 << 30)
|
||||
#define FUSE_INIT_RESERVED (1 << 31)
|
||||
/* bits 32..63 get shifted down 32 bits into the flags2 field */
|
||||
#define FUSE_SECURITY_CTX (1ULL << 32)
|
||||
#define FUSE_HAS_INODE_DAX (1ULL << 33)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@@ -371,11 +421,14 @@ 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
|
||||
* FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits
|
||||
*/
|
||||
#define FUSE_WRITE_CACHE (1 << 0)
|
||||
#define FUSE_WRITE_LOCKOWNER (1 << 1)
|
||||
#define FUSE_WRITE_KILL_PRIV (1 << 2)
|
||||
#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
|
||||
|
||||
/* Obsolete alias; this flag implies killing suid/sgid only. */
|
||||
#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
|
||||
|
||||
/**
|
||||
* Read flags
|
||||
@@ -417,6 +470,27 @@ struct fuse_file_lock {
|
||||
*/
|
||||
#define FUSE_FSYNC_FDATASYNC (1 << 0)
|
||||
|
||||
/**
|
||||
* fuse_attr flags
|
||||
*
|
||||
* FUSE_ATTR_SUBMOUNT: Object is a submount root
|
||||
* FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode
|
||||
*/
|
||||
#define FUSE_ATTR_SUBMOUNT (1 << 0)
|
||||
#define FUSE_ATTR_DAX (1 << 1)
|
||||
|
||||
/**
|
||||
* Open flags
|
||||
* FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable
|
||||
*/
|
||||
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
|
||||
|
||||
/**
|
||||
* setxattr flags
|
||||
* FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
|
||||
*/
|
||||
#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
|
||||
|
||||
enum fuse_opcode {
|
||||
FUSE_LOOKUP = 1,
|
||||
FUSE_FORGET = 2, /* no reply */
|
||||
@@ -465,6 +539,8 @@ enum fuse_opcode {
|
||||
FUSE_COPY_FILE_RANGE = 47,
|
||||
FUSE_SETUPMAPPING = 48,
|
||||
FUSE_REMOVEMAPPING = 49,
|
||||
FUSE_SYNCFS = 50,
|
||||
FUSE_TMPFILE = 51,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
@@ -578,14 +654,14 @@ struct fuse_setattr_in {
|
||||
|
||||
struct fuse_open_in {
|
||||
uint32_t flags;
|
||||
uint32_t unused;
|
||||
uint32_t open_flags; /* FUSE_OPEN_... */
|
||||
};
|
||||
|
||||
struct fuse_create_in {
|
||||
uint32_t flags;
|
||||
uint32_t mode;
|
||||
uint32_t umask;
|
||||
uint32_t padding;
|
||||
uint32_t open_flags; /* FUSE_OPEN_... */
|
||||
};
|
||||
|
||||
struct fuse_open_out {
|
||||
@@ -647,9 +723,13 @@ struct fuse_fsync_in {
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
|
||||
|
||||
struct fuse_setxattr_in {
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
uint32_t setxattr_flags;
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
struct fuse_getxattr_in {
|
||||
@@ -684,6 +764,8 @@ struct fuse_init_in {
|
||||
uint32_t minor;
|
||||
uint32_t max_readahead;
|
||||
uint32_t flags;
|
||||
uint32_t flags2;
|
||||
uint32_t unused[11];
|
||||
};
|
||||
|
||||
#define FUSE_COMPAT_INIT_OUT_SIZE 8
|
||||
@@ -700,7 +782,8 @@ struct fuse_init_out {
|
||||
uint32_t time_gran;
|
||||
uint16_t max_pages;
|
||||
uint16_t map_alignment;
|
||||
uint32_t unused[8];
|
||||
uint32_t flags2;
|
||||
uint32_t unused[7];
|
||||
};
|
||||
|
||||
#define CUSE_INIT_INFO_MAX 4096
|
||||
@@ -808,9 +891,12 @@ struct fuse_dirent {
|
||||
char name[];
|
||||
};
|
||||
|
||||
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
|
||||
#define FUSE_DIRENT_ALIGN(x) \
|
||||
/* Align variable length records to 64bit boundary */
|
||||
#define FUSE_REC_ALIGN(x) \
|
||||
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
|
||||
|
||||
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
|
||||
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
|
||||
#define FUSE_DIRENT_SIZE(d) \
|
||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
||||
|
||||
@@ -869,7 +955,8 @@ struct fuse_notify_retrieve_in {
|
||||
};
|
||||
|
||||
/* Device ioctls: */
|
||||
#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t)
|
||||
#define FUSE_DEV_IOC_MAGIC 229
|
||||
#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
|
||||
|
||||
struct fuse_lseek_in {
|
||||
uint64_t fh;
|
||||
@@ -892,4 +979,60 @@ struct fuse_copy_file_range_in {
|
||||
uint64_t flags;
|
||||
};
|
||||
|
||||
#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0)
|
||||
#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1)
|
||||
struct fuse_setupmapping_in {
|
||||
/* An already open handle */
|
||||
uint64_t fh;
|
||||
/* Offset into the file to start the mapping */
|
||||
uint64_t foffset;
|
||||
/* Length of mapping required */
|
||||
uint64_t len;
|
||||
/* Flags, FUSE_SETUPMAPPING_FLAG_* */
|
||||
uint64_t flags;
|
||||
/* Offset in Memory Window */
|
||||
uint64_t moffset;
|
||||
};
|
||||
|
||||
struct fuse_removemapping_in {
|
||||
/* number of fuse_removemapping_one follows */
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
struct fuse_removemapping_one {
|
||||
/* Offset into the dax window start the unmapping */
|
||||
uint64_t moffset;
|
||||
/* Length of mapping required */
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
#define FUSE_REMOVEMAPPING_MAX_ENTRY \
|
||||
(PAGE_SIZE / sizeof(struct fuse_removemapping_one))
|
||||
|
||||
struct fuse_syncfs_in {
|
||||
uint64_t padding;
|
||||
};
|
||||
|
||||
/*
|
||||
* For each security context, send fuse_secctx with size of security context
|
||||
* fuse_secctx will be followed by security context name and this in turn
|
||||
* will be followed by actual context label.
|
||||
* fuse_secctx, name, context
|
||||
*/
|
||||
struct fuse_secctx {
|
||||
uint32_t size;
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
/*
|
||||
* Contains the information about how many fuse_secctx structures are being
|
||||
* sent and what's the total size of all security contexts (including
|
||||
* size of fuse_secctx_header).
|
||||
*
|
||||
*/
|
||||
struct fuse_secctx_header {
|
||||
uint32_t size;
|
||||
uint32_t nr_secctx;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_FUSE_H */
|
||||
|
||||
+885
-1208
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,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct fuse_chan;
|
||||
struct fuse_ll;
|
||||
|
||||
typedef struct fuse_pollhandle_t fuse_pollhandle_t;
|
||||
struct fuse_pollhandle_t
|
||||
{
|
||||
uint64_t kh;
|
||||
struct fuse_chan *ch;
|
||||
struct fuse_ll *f;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_timeouts_s fuse_timeouts_t;
|
||||
struct fuse_timeouts_s
|
||||
{
|
||||
uint64_t entry;
|
||||
uint64_t attr;
|
||||
};
|
||||
@@ -59,6 +59,10 @@
|
||||
#define kv_pop(v) ((v).a[--(v).n])
|
||||
#define kv_size(v) ((v).n)
|
||||
#define kv_max(v) ((v).m)
|
||||
#define kv_first(v) (kv_A(v,0))
|
||||
#define kv_last(v) (kv_A(v,kv_size(v)-1))
|
||||
#define kv_end(v) (kv_A(v,kv_size(v)))
|
||||
#define kv_delete(v,i) (kv_A(v,i) = kv_pop(v))
|
||||
|
||||
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
+201
-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,63 @@ 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 +224,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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "crc32b.h"
|
||||
|
||||
static
|
||||
const
|
||||
crc32b_t CRC32BTABLE[256] =
|
||||
{
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
crc32b_t
|
||||
crc32b_start(void)
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
crc32b_t
|
||||
crc32b_continue(const void *buf_,
|
||||
const crc32b_t len_,
|
||||
const crc32b_t crc_)
|
||||
{
|
||||
int i;
|
||||
char *buf;
|
||||
crc32b_t crc;
|
||||
|
||||
crc = crc_;
|
||||
buf = (char*)buf_;
|
||||
for(i = 0; i < len_; i++)
|
||||
{
|
||||
crc = (CRC32BTABLE[(crc ^ buf[i]) & 0xFFL] ^ (crc >> 8));
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
crc32b_t
|
||||
crc32b_finish(const crc32b_t crc_)
|
||||
{
|
||||
return (crc_ ^ 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
crc32b_t
|
||||
crc32b(const void *buf_,
|
||||
const crc32b_t len_)
|
||||
{
|
||||
crc32b_t crc;
|
||||
|
||||
crc = crc32b_start();
|
||||
crc = crc32b_continue(buf_,len_,crc);
|
||||
crc = crc32b_finish(crc);
|
||||
|
||||
return crc;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CRC32B_H_INCLUDED
|
||||
#define CRC32B_H_INCLUDED
|
||||
|
||||
typedef unsigned int crc32b_t;
|
||||
|
||||
crc32b_t crc32b_start(void);
|
||||
crc32b_t crc32b_continue(const void *buf,
|
||||
const crc32b_t len,
|
||||
const crc32b_t crc);
|
||||
crc32b_t crc32b_finish(const crc32b_t crc);
|
||||
|
||||
crc32b_t crc32b(const void *buf, crc32b_t len);
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
+1278
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
void debug_fuse_open_out(const uint64_t unique,
|
||||
const struct fuse_open_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_init_in(const struct fuse_init_in *arg);
|
||||
void debug_fuse_init_out(const uint64_t unique,
|
||||
const struct fuse_init_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_entry_out(const uint64_t unique,
|
||||
const struct fuse_entry_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_attr_out(const uint64_t unique,
|
||||
const struct fuse_attr_out *arg,
|
||||
const uint64_t argsize);
|
||||
void debug_fuse_entry_open_out(const uint64_t unique,
|
||||
const struct fuse_entry_out *earg,
|
||||
const struct fuse_open_out *oarg);
|
||||
void debug_fuse_readlink(const uint64_t unique,
|
||||
const char *linkname);
|
||||
void debug_fuse_write_out(const uint64_t unique,
|
||||
const struct fuse_write_out *arg);
|
||||
void debug_fuse_statfs_out(const uint64_t unique,
|
||||
const struct fuse_statfs_out *arg);
|
||||
void debug_fuse_getxattr_out(const uint64_t unique,
|
||||
const struct fuse_getxattr_out *arg);
|
||||
void debug_fuse_lk_out(const uint64_t unique,
|
||||
const struct fuse_lk_out *arg);
|
||||
void debug_fuse_bmap_out(const uint64_t unique,
|
||||
const struct fuse_bmap_out *arg);
|
||||
void debug_fuse_in_header(const struct fuse_in_header *hdr);
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kvec.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ROUND_UP(N,S) ((((N) + (S) - 1) / (S)) * (S))
|
||||
|
||||
typedef kvec_t(void*) slab_kvec_t;
|
||||
|
||||
typedef struct mem_stack_t mem_stack_t;
|
||||
struct mem_stack_t
|
||||
{
|
||||
mem_stack_t *next;
|
||||
};
|
||||
|
||||
typedef struct fmp_t fmp_t;
|
||||
struct fmp_t
|
||||
{
|
||||
mem_stack_t *objs;
|
||||
slab_kvec_t slabs;
|
||||
uint64_t avail_objs;
|
||||
uint64_t obj_size;
|
||||
uint64_t page_size;
|
||||
uint64_t slab_size;
|
||||
};
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_page_size()
|
||||
{
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_init(fmp_t *fmp_,
|
||||
const uint64_t obj_size_,
|
||||
const uint64_t page_multiple_)
|
||||
{
|
||||
kv_init(fmp_->slabs);
|
||||
fmp_->objs = NULL;
|
||||
fmp_->avail_objs = 0;
|
||||
fmp_->obj_size = ROUND_UP(obj_size_,sizeof(void*));
|
||||
fmp_->page_size = fmp_page_size();
|
||||
fmp_->slab_size = (fmp_->page_size * page_multiple_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_slab_count(fmp_t *fmp_)
|
||||
{
|
||||
return kv_size(fmp_->slabs);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_slab_alloc_posix_memalign(fmp_t *fmp_)
|
||||
{
|
||||
int rv;
|
||||
void *mem;
|
||||
const size_t alignment = fmp_->page_size;
|
||||
const size_t size = fmp_->slab_size;
|
||||
|
||||
rv = posix_memalign(&mem,alignment,size);
|
||||
if(rv != 0)
|
||||
return NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_slab_alloc_mmap(fmp_t *fmp_)
|
||||
{
|
||||
void *mem;
|
||||
void *address = NULL;
|
||||
const size_t length = fmp_->slab_size;
|
||||
const int protect = PROT_READ|PROT_WRITE;
|
||||
const int flags = MAP_PRIVATE|MAP_ANONYMOUS;
|
||||
const int filedes = -1;
|
||||
const off_t offset = 0;
|
||||
|
||||
mem = mmap(address,length,protect,flags,filedes,offset);
|
||||
if(mem == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_slab_free_posix_memalign(fmp_t* fmp_,
|
||||
void *mem_)
|
||||
{
|
||||
(void)fmp_;
|
||||
free(mem_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_slab_free_mmap(fmp_t* fmp_,
|
||||
void *mem_)
|
||||
{
|
||||
void *addr = mem_;
|
||||
size_t length = fmp_->slab_size;
|
||||
|
||||
(void)munmap(addr,length);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
fmp_slab_alloc(fmp_t *fmp_)
|
||||
{
|
||||
char *i;
|
||||
void *mem;
|
||||
|
||||
mem = fmp_slab_alloc_mmap(fmp_);
|
||||
if(mem == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kv_push(void*,fmp_->slabs,mem);
|
||||
|
||||
i = ((char*)mem + fmp_->slab_size - fmp_->obj_size);
|
||||
while(i >= (char*)mem)
|
||||
{
|
||||
mem_stack_t *obj = (mem_stack_t*)i;
|
||||
|
||||
obj->next = fmp_->objs;
|
||||
fmp_->objs = obj;
|
||||
fmp_->avail_objs++;
|
||||
|
||||
i -= fmp_->obj_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_alloc(fmp_t *fmp_)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
if(fmp_->objs == NULL)
|
||||
fmp_slab_alloc(fmp_);
|
||||
if(fmp_->objs == NULL)
|
||||
return NULL;
|
||||
|
||||
rv = fmp_->objs;
|
||||
|
||||
fmp_->objs = fmp_->objs->next;
|
||||
fmp_->avail_objs--;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
fmp_calloc(fmp_t *fmp_)
|
||||
{
|
||||
void *obj;
|
||||
|
||||
obj = fmp_alloc(fmp_);
|
||||
if(obj == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(obj,0,fmp_->obj_size);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_free(fmp_t *fmp_,
|
||||
void *obj_)
|
||||
{
|
||||
mem_stack_t *obj = (mem_stack_t*)obj_;
|
||||
|
||||
obj->next = fmp_->objs;
|
||||
fmp_->objs = obj;
|
||||
fmp_->avail_objs++;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_clear(fmp_t *fmp_)
|
||||
{
|
||||
while(kv_size(fmp_->slabs))
|
||||
{
|
||||
void *slab = kv_pop(fmp_->slabs);
|
||||
|
||||
fmp_slab_free_mmap(fmp_,slab);
|
||||
}
|
||||
|
||||
fmp_->objs = NULL;
|
||||
fmp_->avail_objs = 0;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_destroy(fmp_t *fmp_)
|
||||
{
|
||||
fmp_clear(fmp_);
|
||||
kv_destroy(fmp_->slabs);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_avail_objs(fmp_t *fmp_)
|
||||
{
|
||||
return fmp_->avail_objs;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_objs_per_slab(fmp_t *fmp_)
|
||||
{
|
||||
return (fmp_->slab_size / fmp_->obj_size);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_objs_in_slab(fmp_t *fmp_,
|
||||
void *slab_)
|
||||
{
|
||||
char *slab;
|
||||
uint64_t objs_per_slab;
|
||||
uint64_t objs_in_slab;
|
||||
|
||||
slab = (char*)slab_;
|
||||
objs_in_slab = 0;
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
for(mem_stack_t *stack = fmp_->objs; stack != NULL; stack = stack->next)
|
||||
{
|
||||
char *obj = (char*)stack;
|
||||
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
|
||||
objs_in_slab++;
|
||||
if(objs_in_slab >= objs_per_slab)
|
||||
break;
|
||||
}
|
||||
|
||||
return objs_in_slab;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fmp_remove_objs_in_slab(fmp_t *fmp_,
|
||||
void *slab_)
|
||||
{
|
||||
char *slab;
|
||||
uint64_t objs_per_slab;
|
||||
uint64_t objs_in_slab;
|
||||
mem_stack_t **p;
|
||||
|
||||
p = &fmp_->objs;
|
||||
slab = (char*)slab_;
|
||||
objs_in_slab = 0;
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
while((*p) != NULL)
|
||||
{
|
||||
char *obj = (char*)*p;
|
||||
|
||||
if((obj >= slab) && (obj < (slab + fmp_->slab_size)))
|
||||
{
|
||||
objs_in_slab++;
|
||||
*p = (*p)->next;
|
||||
fmp_->avail_objs--;
|
||||
|
||||
if(objs_in_slab >= objs_per_slab)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
p = &(*p)->next;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
fmp_gc_slab(fmp_t *fmp_,
|
||||
uint64_t slab_idx_)
|
||||
{
|
||||
char *slab;
|
||||
uint64_t objs_in_slab;
|
||||
uint64_t objs_per_slab;
|
||||
|
||||
slab_idx_ = (slab_idx_ % kv_size(fmp_->slabs));
|
||||
|
||||
slab = kv_A(fmp_->slabs,slab_idx_);
|
||||
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
objs_in_slab = fmp_objs_in_slab(fmp_,slab);
|
||||
if(objs_in_slab != objs_per_slab)
|
||||
return 0;
|
||||
|
||||
fmp_remove_objs_in_slab(fmp_,slab);
|
||||
kv_delete(fmp_->slabs,slab_idx_);
|
||||
fmp_slab_free_mmap(fmp_,slab);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
fmp_gc(fmp_t *fmp_)
|
||||
{
|
||||
uint64_t slab_idx;
|
||||
|
||||
slab_idx = rand();
|
||||
|
||||
return fmp_gc_slab(fmp_,slab_idx);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
double
|
||||
fmp_slab_usage_ratio(fmp_t *fmp_)
|
||||
{
|
||||
double avail_objs;
|
||||
double objs_per_slab;
|
||||
double nums_of_slabs;
|
||||
|
||||
avail_objs = fmp_->avail_objs;
|
||||
objs_per_slab = fmp_objs_per_slab(fmp_);
|
||||
nums_of_slabs = kv_size(fmp_->slabs);
|
||||
|
||||
return (avail_objs / (objs_per_slab * nums_of_slabs));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
fmp_total_allocated_memory(fmp_t *fmp_)
|
||||
{
|
||||
return (fmp_->slab_size * kv_size(fmp_->slabs));
|
||||
}
|
||||
+3290
-3930
File diff suppressed because it is too large
Load Diff
+33
-37
@@ -19,6 +19,14 @@
|
||||
/* 32KB - same as glibc getdents buffer size */
|
||||
#define DEFAULT_SIZE (1024 * 32)
|
||||
|
||||
static
|
||||
uint64_t
|
||||
round_up(const uint64_t number_,
|
||||
const uint64_t multiple_)
|
||||
{
|
||||
return (((number_ + multiple_ - 1) / multiple_) * multiple_);
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t
|
||||
align_uint64_t(uint64_t v_)
|
||||
@@ -57,16 +65,15 @@ int
|
||||
fuse_dirents_buf_resize(fuse_dirents_t *d_,
|
||||
uint64_t size_)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if((d_->data_len + size_) >= d_->buf_len)
|
||||
if((kv_size(d_->data) + size_) >= kv_max(d_->data))
|
||||
{
|
||||
p = realloc(d_->buf,(d_->buf_len * 2));
|
||||
if(p == NULL)
|
||||
return -errno;
|
||||
uint64_t new_size;
|
||||
|
||||
d_->buf = p;
|
||||
d_->buf_len *= 2;
|
||||
new_size = round_up((kv_size(d_->data) + size_),DEFAULT_SIZE);
|
||||
|
||||
kv_resize(char,d_->data,new_size);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -87,9 +94,8 @@ fuse_dirents_dirent_alloc(fuse_dirents_t *d_,
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
|
||||
d_->data_len += size;
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
@@ -109,9 +115,8 @@ fuse_dirents_direntplus_alloc(fuse_dirents_t *d_,
|
||||
if(rv)
|
||||
return NULL;
|
||||
|
||||
d = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
|
||||
d_->data_len += size;
|
||||
d = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
kv_size(d_->data) += size;
|
||||
|
||||
return d;
|
||||
}
|
||||
@@ -170,8 +175,8 @@ fuse_dirent_find(fuse_dirents_t *d_,
|
||||
if(d_->type != NORMAL)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_dirent_t*)&d_->buf[0];
|
||||
end = (fuse_dirent_t*)&d_->buf[d_->data_len];
|
||||
cur = (fuse_dirent_t*)&kv_first(d_->data);
|
||||
end = (fuse_dirent_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->ino == ino_)
|
||||
@@ -193,8 +198,8 @@ fuse_direntplus_find(fuse_dirents_t *d_,
|
||||
if(d_->type != PLUS)
|
||||
return NULL;
|
||||
|
||||
cur = (fuse_direntplus_t*)&d_->buf[0];
|
||||
end = (fuse_direntplus_t*)&d_->buf[d_->data_len];
|
||||
cur = (fuse_direntplus_t*)&kv_first(d_->data);
|
||||
end = (fuse_direntplus_t*)&kv_end(d_->data);
|
||||
while(cur < end)
|
||||
{
|
||||
if(cur->dirent.ino == ino_)
|
||||
@@ -251,7 +256,7 @@ fuse_dirents_add(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->d_ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->d_type;
|
||||
@@ -285,7 +290,7 @@ fuse_dirents_add_plus(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->dirent.ino = dirent_->d_ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->d_type;
|
||||
@@ -321,7 +326,7 @@ fuse_dirents_add_linux(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->ino = dirent_->ino;
|
||||
d->namelen = namelen_;
|
||||
d->type = dirent_->type;
|
||||
@@ -355,7 +360,7 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
return -ENOMEM;
|
||||
|
||||
d->dirent.off = kv_size(d_->offs);
|
||||
kv_push(uint32_t,d_->offs,d_->data_len);
|
||||
kv_push(uint32_t,d_->offs,kv_size(d_->data));
|
||||
d->dirent.ino = dirent_->ino;
|
||||
d->dirent.namelen = namelen_;
|
||||
d->dirent.type = dirent_->type;
|
||||
@@ -371,25 +376,21 @@ fuse_dirents_add_linux_plus(fuse_dirents_t *d_,
|
||||
void
|
||||
fuse_dirents_reset(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
kv_size(d_->data) = 0;
|
||||
kv_size(d_->offs) = 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_dirents_init(fuse_dirents_t *d_)
|
||||
{
|
||||
void *buf;
|
||||
d_->type = UNSET;
|
||||
|
||||
buf = calloc(DEFAULT_SIZE,1);
|
||||
if(buf == NULL)
|
||||
kv_init(d_->data);
|
||||
kv_resize(char,d_->data,DEFAULT_SIZE);
|
||||
if(d_->data.a == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
d_->buf = buf;
|
||||
d_->buf_len = DEFAULT_SIZE;
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_init(d_->offs);
|
||||
kv_resize(uint32_t,d_->offs,64);
|
||||
kv_push(uint32_t,d_->offs,0);
|
||||
@@ -400,11 +401,6 @@ fuse_dirents_init(fuse_dirents_t *d_)
|
||||
void
|
||||
fuse_dirents_free(fuse_dirents_t *d_)
|
||||
{
|
||||
d_->buf_len = 0;
|
||||
d_->data_len = 0;
|
||||
d_->type = UNSET;
|
||||
|
||||
kv_destroy(d_->data);
|
||||
kv_destroy(d_->offs);
|
||||
|
||||
free(d_->buf);
|
||||
}
|
||||
|
||||
+68
-80
@@ -6,106 +6,97 @@
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include "extern_c.h"
|
||||
|
||||
struct fuse_chan;
|
||||
struct fuse_ll;
|
||||
|
||||
struct fuse_session {
|
||||
struct fuse_session_ops op;
|
||||
struct fuse_session
|
||||
{
|
||||
int (*receive_buf)(struct fuse_session *se,
|
||||
struct fuse_buf *buf,
|
||||
struct fuse_chan *ch);
|
||||
|
||||
int (*receive_buf)(struct fuse_session *se, struct fuse_buf *buf,
|
||||
struct fuse_chan **chp);
|
||||
void (*process_buf)(void *data,
|
||||
const void *buf,
|
||||
struct fuse_chan *ch);
|
||||
|
||||
void (*process_buf)(void *data, const struct fuse_buf *buf,
|
||||
struct fuse_chan *ch);
|
||||
void (*destroy)(void *data);
|
||||
|
||||
void *data;
|
||||
|
||||
volatile int exited;
|
||||
|
||||
struct fuse_chan *ch;
|
||||
void *data;
|
||||
volatile int exited;
|
||||
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;
|
||||
struct fuse_ctx ctx;
|
||||
struct fuse_chan *ch;
|
||||
unsigned int ioctl_64bit : 1;
|
||||
};
|
||||
|
||||
struct fuse_notify_req {
|
||||
uint64_t unique;
|
||||
void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_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,
|
||||
uint64_t,
|
||||
const void *);
|
||||
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 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;
|
||||
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;
|
||||
};
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size, void *user_data, int compat);
|
||||
|
||||
int fuse_sync_compat_args(struct fuse_args *args);
|
||||
size_t op_size);
|
||||
|
||||
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 +111,8 @@ 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);
|
||||
int *fd);
|
||||
|
||||
int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg);
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
@@ -1,103 +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 "fuse_kernel.h"
|
||||
#include "fuse_i.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
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);
|
||||
|
||||
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 (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 (res == -1) {
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
|
||||
assert(se != NULL);
|
||||
|
||||
/* 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);
|
||||
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
struct fuse_chan *
|
||||
fuse_kern_chan_new(int fd_)
|
||||
{
|
||||
long pagesize;
|
||||
size_t bufsize;
|
||||
struct fuse_chan_ops op =
|
||||
{
|
||||
.receive = fuse_kern_chan_receive,
|
||||
.send = fuse_kern_chan_send,
|
||||
.destroy = fuse_kern_chan_destroy,
|
||||
};
|
||||
|
||||
pagesize = sysconf(_SC_PAGESIZE);
|
||||
|
||||
bufsize = ((FUSE_MAX_MAX_PAGES * pagesize) + 0x1000);
|
||||
|
||||
return fuse_chan_new(&op, fd_, bufsize, NULL);
|
||||
}
|
||||
@@ -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*)calloc(bufsize,1);
|
||||
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;
|
||||
}
|
||||
@@ -1,225 +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 "fuse_misc.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_i.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <semaphore.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Environment var controlling the thread stack size */
|
||||
#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_mt {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (mt->exit)
|
||||
return NULL;
|
||||
|
||||
fuse_session_process_buf(mt->se, &fbuf, ch);
|
||||
}
|
||||
|
||||
sem_post(&mt->finish);
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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 = 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int number_of_threads(void)
|
||||
{
|
||||
#ifdef _SC_NPROCESSORS_ONLN
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
err = mt.error;
|
||||
}
|
||||
|
||||
sem_destroy(&mt.finish);
|
||||
fuse_session_reset(se);
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
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 "pool.hpp"
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_misc.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Environment var controlling the thread stack size */
|
||||
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
|
||||
|
||||
struct fuse_worker
|
||||
{
|
||||
struct fuse_worker *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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int fuse_loop_start_thread(struct fuse_mt *mt);
|
||||
|
||||
thread_pool tp;
|
||||
|
||||
static
|
||||
void*
|
||||
fuse_do_work(void *data)
|
||||
{
|
||||
struct fuse_worker *w = (struct fuse_worker *) data;
|
||||
struct fuse_mt *mt = w->mt;
|
||||
|
||||
while(!fuse_session_exited(mt->se))
|
||||
{
|
||||
int res;
|
||||
struct fuse_buf fbuf = {0};
|
||||
|
||||
fbuf.mem = (char*)malloc(w->bufsize);
|
||||
fbuf.size = w->bufsize;
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
res = mt->se->receive_buf(mt->se,&fbuf,mt->se->ch);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
if(res == -EINTR)
|
||||
continue;
|
||||
if(res <= 0)
|
||||
{
|
||||
if(res < 0)
|
||||
{
|
||||
mt->se->exited = 1;
|
||||
mt->error = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(mt->exit)
|
||||
return NULL;
|
||||
|
||||
tp.enqueue_work([=] {
|
||||
mt->se->process_buf(mt->se->data,fbuf.mem,mt->se->ch);
|
||||
|
||||
free(fbuf.mem);
|
||||
});
|
||||
}
|
||||
|
||||
sem_post(&mt->finish);
|
||||
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
sigfillset(&newset);
|
||||
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;
|
||||
}
|
||||
|
||||
static int fuse_loop_start_thread(struct fuse_mt *mt)
|
||||
{
|
||||
int res;
|
||||
struct fuse_worker *w = (struct fuse_worker*)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->se->ch);
|
||||
w->buf = (char*)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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int number_of_threads(void)
|
||||
{
|
||||
#ifdef _SC_NPROCESSORS_ONLN
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
memset(&mt,0,sizeof(struct fuse_mt));
|
||||
mt.se = se_;
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
err = mt.error;
|
||||
}
|
||||
|
||||
sem_destroy(&mt.finish);
|
||||
fuse_session_reset(se_);
|
||||
|
||||
return err;
|
||||
}
|
||||
+1786
-2544
File diff suppressed because it is too large
Load Diff
+10
-7
@@ -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,11 +29,11 @@
|
||||
/* 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
|
||||
|
||||
|
||||
+14
-103
@@ -16,109 +16,20 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
static void mt_session_proc(void *data, const char *buf, size_t len,
|
||||
struct fuse_chan *ch)
|
||||
int
|
||||
fuse_loop_mt(struct fuse *f)
|
||||
{
|
||||
struct procdata *pd = (struct procdata *) data;
|
||||
struct fuse_cmd *cmd = *(struct fuse_cmd **) buf;
|
||||
if (f == NULL)
|
||||
return -1;
|
||||
|
||||
(void) len;
|
||||
(void) ch;
|
||||
pd->proc(pd->f, cmd, pd->data);
|
||||
int res = fuse_start_maintenance_thread(f);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
res = fuse_session_loop_mt(fuse_get_session(f),
|
||||
fuse_config_num_threads(f));
|
||||
|
||||
fuse_stop_maintenance_thread(f);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int mt_session_exited(void *data)
|
||||
{
|
||||
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);
|
||||
|
||||
assert(size >= sizeof(cmd));
|
||||
|
||||
cmd = fuse_read_cmd(pd->f);
|
||||
if (cmd == NULL)
|
||||
return 0;
|
||||
|
||||
*(struct fuse_cmd **) buf = 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,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
FUSE_SYMVER(".symver fuse_loop_mt_proc,__fuse_loop_mt@");
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#include "fuse_node.h"
|
||||
|
||||
#include "khash.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <stdio.h> // for debugging
|
||||
|
||||
#define UNKNOWN_INO UINT64_MAX
|
||||
#define ROOT_NODE_ID 0
|
||||
#define ROOT_NODE_NAME "/"
|
||||
|
||||
typedef struct node_idname_t node_idname_t;
|
||||
struct node_idname_t
|
||||
{
|
||||
uint64_t id;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static khint_t idname_hash_func(const node_idname_t idname);
|
||||
static int idname_hash_equal(const node_idname_t idname0, const node_idname_t idname1);
|
||||
|
||||
KHASH_INIT(node,node_idname_t,fuse_node_t*,1,idname_hash_func,idname_hash_equal);
|
||||
|
||||
typedef struct fuse_node_hashtable_t fuse_node_hashtable_t;
|
||||
struct fuse_node_hashtable_t
|
||||
{
|
||||
kh_node_t *ht;
|
||||
uint64_t id;
|
||||
uint64_t generation;
|
||||
};
|
||||
|
||||
static
|
||||
inline
|
||||
khint_t
|
||||
idname_hash_func(const node_idname_t idname_)
|
||||
{
|
||||
if(idname_.name == NULL)
|
||||
return idname_.id;
|
||||
return (idname_.id ^ kh_str_hash_func(idname_.name));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
idname_hash_equal(const node_idname_t idname0_,
|
||||
const node_idname_t idname1_)
|
||||
{
|
||||
return ((idname0_.id == idname1_.id) &&
|
||||
((idname0_.name == idname1_.name) ||
|
||||
(strcmp(idname0_.name,idname1_.name) == 0)));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
fuse_node_t*
|
||||
fuse_node_alloc(const uint64_t id_,
|
||||
const char *name_)
|
||||
{
|
||||
fuse_node_t *node;
|
||||
|
||||
node = (fuse_node_t*)calloc(1,sizeof(fuse_node_t));
|
||||
|
||||
node->id = id_;
|
||||
node->name = strdup(name_);
|
||||
node->ref_count = 1;
|
||||
node->lookup_count = 1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
fuse_node_free(fuse_node_t *node_)
|
||||
{
|
||||
free(node_->name);
|
||||
free(node_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
rand64()
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
rv = rand();
|
||||
rv <<= 32;
|
||||
rv |= rand();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
node_hashtable_gen_unique_id(fuse_node_hashtable_t *ht_)
|
||||
{
|
||||
do
|
||||
{
|
||||
ht_->id++;
|
||||
if(ht_->id == 0)
|
||||
ht_->generation++;
|
||||
}
|
||||
while((ht_->id == 0) || (ht_->id == UNKNOWN_INO));
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
node_hashtable_put_root(fuse_node_hashtable_t *ht_)
|
||||
{
|
||||
int rv;
|
||||
khint_t k;
|
||||
fuse_node_t *root_node;
|
||||
const node_idname_t idname0 = {ROOT_NODE_ID,""};
|
||||
const node_idname_t idname1 = {ROOT_NODE_ID,ROOT_NODE_NAME};
|
||||
|
||||
root_node = fuse_node_alloc(ROOT_NODE_ID,ROOT_NODE_NAME);
|
||||
|
||||
k = kh_put_node(ht_->ht,idname0,&rv);
|
||||
kh_value(ht_->ht,k) = root_node;
|
||||
|
||||
k = kh_put_node(ht_->ht,idname1,&rv);
|
||||
kh_value(ht_->ht,k) = root_node;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
node_hashtable_set_id_gen(fuse_node_hashtable_t *ht_,
|
||||
fuse_node_t *node_)
|
||||
{
|
||||
node_hashtable_gen_unique_id(ht_);
|
||||
node_->id = ht_->id;
|
||||
node_->generation = ht_->generation;
|
||||
}
|
||||
|
||||
fuse_node_hashtable_t*
|
||||
fuse_node_hashtable_init()
|
||||
{
|
||||
fuse_node_hashtable_t *ht;
|
||||
|
||||
ht = (fuse_node_hashtable_t*)calloc(sizeof(fuse_node_hashtable_t),1);
|
||||
if(ht == NULL)
|
||||
return NULL;
|
||||
|
||||
ht->ht = kh_init_node();
|
||||
if(ht->ht == NULL)
|
||||
{
|
||||
free(ht);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
ht->id = 0;
|
||||
ht->generation = rand64();
|
||||
|
||||
node_hashtable_put_root(ht);
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
fuse_node_t*
|
||||
fuse_node_hashtable_put(fuse_node_hashtable_t *ht_,
|
||||
const uint64_t parent_id_,
|
||||
const uint64_t child_id_,
|
||||
const char *child_name_)
|
||||
{
|
||||
int rv;
|
||||
khint_t k;
|
||||
fuse_node_t *child_node;
|
||||
const node_idname_t p_idname = {parent_id_,""};
|
||||
const node_idname_t c0_idname = {child_id_,child_name_};
|
||||
const node_idname_t c1_idname = {parent_id_,child_name_};
|
||||
|
||||
child_node = fuse_node_alloc(child_id_,child_name_);
|
||||
|
||||
k = kh_get_node(ht_->ht,p_idname);
|
||||
child_node->parent = kh_value(ht_->ht,k);
|
||||
child_node->parent->ref_count++;
|
||||
|
||||
k = kh_put_node(ht_->ht,c0_idname,&rv);
|
||||
kh_value(ht_->ht,k) = child_node;
|
||||
|
||||
k = kh_put_node(ht_->ht,c1_idname,&rv);
|
||||
kh_value(ht_->ht,k) = child_node;
|
||||
|
||||
return child_node;
|
||||
}
|
||||
|
||||
fuse_node_t*
|
||||
fuse_node_hashtable_get(fuse_node_hashtable_t *ht_,
|
||||
const uint64_t id_)
|
||||
{
|
||||
return fuse_node_hashtable_get_child(ht_,id_,"");
|
||||
}
|
||||
|
||||
fuse_node_t*
|
||||
fuse_node_hashtable_get_child(fuse_node_hashtable_t *ht_,
|
||||
const uint64_t parent_id_,
|
||||
const char *child_name_)
|
||||
{
|
||||
khint_t k;
|
||||
fuse_node_t *node;
|
||||
const node_idname_t idname = {parent_id_,child_name_};
|
||||
|
||||
k = kh_get_node(ht_->ht,idname);
|
||||
node = ((k != kh_end(ht_->ht)) ?
|
||||
kh_value(ht_->ht,k) :
|
||||
NULL);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_node_hashtable_del(fuse_node_hashtable_t *ht_,
|
||||
fuse_node_t *node_)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct fuse_node_t fuse_node_t;
|
||||
struct fuse_node_t
|
||||
{
|
||||
uint64_t id;
|
||||
uint64_t generation;
|
||||
char *name;
|
||||
fuse_node_t *parent;
|
||||
uint32_t ref_count;
|
||||
uint64_t lookup_count;
|
||||
uint64_t open_count;
|
||||
};
|
||||
|
||||
struct fuse_node_hashtable_t;
|
||||
typedef struct fuse_node_hashtable_t fuse_node_hashtable_t;
|
||||
|
||||
|
||||
fuse_node_hashtable_t *fuse_node_hashtable_init();
|
||||
|
||||
fuse_node_t *fuse_node_hashtable_put(fuse_node_hashtable_t *ht,
|
||||
const uint64_t parent_id,
|
||||
const uint64_t child_id,
|
||||
const char *child_name);
|
||||
|
||||
fuse_node_t* fuse_node_hashtable_get(fuse_node_hashtable_t *ht,
|
||||
const uint64_t id);
|
||||
fuse_node_t* fuse_node_hashtable_get_child(fuse_node_hashtable_t *ht,
|
||||
const uint64_t id,
|
||||
const char *name);
|
||||
void fuse_node_hashtable_del(fuse_node_hashtable_t *ht,
|
||||
fuse_node_t *node);
|
||||
|
||||
void fuse_node_hashtable_get_path(fuse_node_hashtable_t *ht,
|
||||
char *buf,
|
||||
uint32_t buflen);
|
||||
+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");
|
||||
|
||||
+151
-162
@@ -8,233 +8,222 @@
|
||||
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_common_compat.h"
|
||||
#include "fuse_lowlevel_compat.h"
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.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_session *se;
|
||||
int fd;
|
||||
size_t bufsize;
|
||||
};
|
||||
|
||||
struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data)
|
||||
struct fuse_session *fuse_session_new(void *data,
|
||||
void *receive_buf,
|
||||
void *process_buf,
|
||||
void *destroy)
|
||||
{
|
||||
struct fuse_session *se = (struct fuse_session *) malloc(sizeof(*se));
|
||||
if (se == NULL) {
|
||||
fprintf(stderr, "fuse: failed to allocate session\n");
|
||||
return NULL;
|
||||
}
|
||||
struct fuse_session *se = (struct fuse_session *) 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->data = data;
|
||||
se->receive_buf = receive_buf;
|
||||
se->process_buf = process_buf;
|
||||
se->destroy = destroy;
|
||||
|
||||
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)
|
||||
void
|
||||
fuse_session_destroy(struct fuse_session *se)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf,
|
||||
struct fuse_chan **chp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void fuse_session_exit(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 1);
|
||||
se->exited = 1;
|
||||
se->destroy(se->data);
|
||||
if(se->ch != NULL)
|
||||
fuse_chan_destroy(se->ch);
|
||||
free(se);
|
||||
}
|
||||
|
||||
void fuse_session_reset(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exit)
|
||||
se->op.exit(se->data, 0);
|
||||
se->exited = 0;
|
||||
se->exited = 0;
|
||||
}
|
||||
|
||||
int fuse_session_exited(struct fuse_session *se)
|
||||
int
|
||||
fuse_session_exited(struct fuse_session *se)
|
||||
{
|
||||
if (se->op.exited)
|
||||
return se->op.exited(se->data);
|
||||
else
|
||||
return se->exited;
|
||||
return se->exited;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_session_exit(struct fuse_session *se_)
|
||||
{
|
||||
se_->exited = 1;
|
||||
}
|
||||
|
||||
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)
|
||||
struct fuse_chan *
|
||||
fuse_chan_new(int fd,
|
||||
size_t bufsize)
|
||||
{
|
||||
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;
|
||||
|
||||
memset(ch, 0, sizeof(*ch));
|
||||
ch->op = *op;
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
ch->data = data;
|
||||
ch->compat = compat;
|
||||
ch = (struct fuse_chan*)malloc(sizeof(*ch));
|
||||
if(ch == NULL)
|
||||
{
|
||||
fprintf(stderr, "fuse: failed to allocate channel\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
memset(ch, 0, sizeof(*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);
|
||||
}
|
||||
ch->fd = fd;
|
||||
ch->bufsize = bufsize;
|
||||
|
||||
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 ch;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void *fuse_chan_data(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->data;
|
||||
return ch->bufsize;
|
||||
}
|
||||
|
||||
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)
|
||||
int
|
||||
fuse_chan_recv(struct fuse_chan *ch,
|
||||
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);
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
se->exited = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size)
|
||||
int
|
||||
fuse_chan_send(struct fuse_chan *ch,
|
||||
const struct iovec iov[],
|
||||
size_t count)
|
||||
{
|
||||
int res;
|
||||
if(!iov)
|
||||
return 0;
|
||||
|
||||
res = fuse_chan_recv(&ch, buf, size);
|
||||
return res >= 0 ? res : (res != -EINTR && res != -EAGAIN) ? -1 : 0;
|
||||
int err;
|
||||
ssize_t res;
|
||||
|
||||
res = writev(fuse_chan_fd(ch), iov, count);
|
||||
err = errno;
|
||||
|
||||
if(res == -1)
|
||||
{
|
||||
struct fuse_session *se = fuse_chan_session(ch);
|
||||
|
||||
assert(se != NULL);
|
||||
|
||||
/* ENOENT means the operation was interrupted */
|
||||
if(!fuse_session_exited(se) && err != ENOENT)
|
||||
perror("fuse: writing device");
|
||||
return -err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count)
|
||||
void
|
||||
fuse_chan_destroy(struct fuse_chan *ch)
|
||||
{
|
||||
return ch->op.send(ch, iov, count);
|
||||
}
|
||||
int fd;
|
||||
|
||||
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);
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
FUSE_SYMVER(".symver fuse_chan_new_compat24,fuse_chan_new@FUSE_2.4");
|
||||
#endif
|
||||
fd = fuse_chan_fd(ch);
|
||||
if(fd != -1)
|
||||
close(fd);
|
||||
|
||||
free(ch);
|
||||
}
|
||||
|
||||
+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;
|
||||
+205
-309
@@ -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,110 @@ 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("-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 +170,6 @@ fuse_parse_cmdline(struct fuse_args *args_,
|
||||
else
|
||||
free(hopts.mountpoint);
|
||||
|
||||
if(multithreaded_)
|
||||
*multithreaded_ = !hopts.singlethread;
|
||||
if(foreground_)
|
||||
*foreground_ = hopts.foreground;
|
||||
|
||||
@@ -191,54 +182,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
|
||||
@@ -246,8 +237,10 @@ struct fuse_chan *
|
||||
fuse_mount_common(const char *mountpoint_,
|
||||
struct fuse_args *args_)
|
||||
{
|
||||
struct fuse_chan *ch;
|
||||
int fd;
|
||||
long bufsize;
|
||||
long pagesize;
|
||||
struct fuse_chan *ch;
|
||||
|
||||
/*
|
||||
* Make sure file descriptors 0, 1 and 2 are open, otherwise chaos
|
||||
@@ -260,11 +253,14 @@ 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;
|
||||
|
||||
ch = fuse_kern_chan_new(fd);
|
||||
pagesize = sysconf(_SC_PAGESIZE);
|
||||
bufsize = ((FUSE_MAX_MAX_PAGES + 1) * pagesize);
|
||||
|
||||
ch = fuse_chan_new(fd,bufsize);
|
||||
if(!ch)
|
||||
fuse_kern_unmount(mountpoint_, fd);
|
||||
|
||||
@@ -280,232 +276,132 @@ 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)
|
||||
struct fuse *
|
||||
fuse_setup_common(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size,
|
||||
char **mountpoint,
|
||||
int *fd)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 = se->ch;
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_unmount_common(mountpoint, ch);
|
||||
fuse_destroy(fuse);
|
||||
free(mountpoint);
|
||||
}
|
||||
|
||||
void fuse_teardown(struct fuse *fuse, char *mountpoint)
|
||||
{
|
||||
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)
|
||||
const struct fuse_operations *op, size_t op_size)
|
||||
{
|
||||
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);
|
||||
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)
|
||||
int fuse_main_real(int argc,
|
||||
char *argv[],
|
||||
const struct fuse_operations *op,
|
||||
size_t op_size)
|
||||
{
|
||||
return fuse_main_common(argc, argv, op, op_size, user_data, 0);
|
||||
return fuse_main_common(argc, argv, op, op_size);
|
||||
}
|
||||
|
||||
#undef fuse_main
|
||||
int fuse_main(void);
|
||||
int fuse_main(void)
|
||||
{
|
||||
fprintf(stderr, "fuse_main(): This function does not exist\n");
|
||||
return -1;
|
||||
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");
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
An example:
|
||||
|
||||
#include "khash.h"
|
||||
KHASH_MAP_INIT_INT(32, char)
|
||||
int main() {
|
||||
int ret, is_missing;
|
||||
khiter_t k;
|
||||
khash_t(32) *h = kh_init(32);
|
||||
k = kh_put(32, h, 5, &ret);
|
||||
kh_value(h, k) = 10;
|
||||
k = kh_get(32, h, 10);
|
||||
is_missing = (k == kh_end(h));
|
||||
k = kh_get(32, h, 5);
|
||||
kh_del(32, h, k);
|
||||
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||
kh_destroy(32, h);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2013-05-02 (0.2.8):
|
||||
|
||||
* Use quadratic probing. When the capacity is power of 2, stepping function
|
||||
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
|
||||
hashing on cache performance and is more robust than linear probing.
|
||||
|
||||
In theory, double hashing should be more robust than quadratic probing.
|
||||
However, my implementation is probably not for large hash tables, because
|
||||
the second hash function is closely tied to the first hash function,
|
||||
which reduce the effectiveness of double hashing.
|
||||
|
||||
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
|
||||
|
||||
2011-12-29 (0.2.7):
|
||||
|
||||
* Minor code clean up; no actual effect.
|
||||
|
||||
2011-09-16 (0.2.6):
|
||||
|
||||
* The capacity is a power of 2. This seems to dramatically improve the
|
||||
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||
|
||||
- http://code.google.com/p/ulib/
|
||||
- http://nothings.org/computer/judy/
|
||||
|
||||
* Allow to optionally use linear probing which usually has better
|
||||
performance for random input. Double hashing is still the default as it
|
||||
is more robust to certain non-random input.
|
||||
|
||||
* Added Wang's integer hash function (not used by default). This hash
|
||||
function is more robust to certain non-random input.
|
||||
|
||||
2011-02-14 (0.2.5):
|
||||
|
||||
* Allow to declare global functions.
|
||||
|
||||
2009-09-26 (0.2.4):
|
||||
|
||||
* Improve portability
|
||||
|
||||
2008-09-19 (0.2.3):
|
||||
|
||||
* Corrected the example
|
||||
* Improved interfaces
|
||||
|
||||
2008-09-11 (0.2.2):
|
||||
|
||||
* Improved speed a little in kh_put()
|
||||
|
||||
2008-09-10 (0.2.1):
|
||||
|
||||
* Added kh_clear()
|
||||
* Fixed a compiling error
|
||||
|
||||
2008-09-02 (0.2.0):
|
||||
|
||||
* Changed to token concatenation which increases flexibility.
|
||||
|
||||
2008-08-31 (0.1.2):
|
||||
|
||||
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||
|
||||
2008-08-31 (0.1.1):
|
||||
|
||||
* Added destructor
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AC_KHASH_H
|
||||
#define __AC_KHASH_H
|
||||
|
||||
/*!
|
||||
@header
|
||||
|
||||
Generic hash table library.
|
||||
*/
|
||||
|
||||
#define AC_VERSION_KHASH_H "0.2.8"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* compiler specific configuration */
|
||||
|
||||
#if UINT_MAX == 0xffffffffu
|
||||
typedef unsigned int khint32_t;
|
||||
#elif ULONG_MAX == 0xffffffffu
|
||||
typedef unsigned long khint32_t;
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == ULLONG_MAX
|
||||
typedef unsigned long khint64_t;
|
||||
#else
|
||||
typedef unsigned long long khint64_t;
|
||||
#endif
|
||||
|
||||
#ifndef kh_inline
|
||||
#ifdef _MSC_VER
|
||||
#define kh_inline __inline
|
||||
#else
|
||||
#define kh_inline inline
|
||||
#endif
|
||||
#endif /* kh_inline */
|
||||
|
||||
#ifndef klib_unused
|
||||
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
|
||||
#define klib_unused __attribute__ ((__unused__))
|
||||
#else
|
||||
#define klib_unused
|
||||
#endif
|
||||
#endif /* klib_unused */
|
||||
|
||||
typedef khint32_t khint_t;
|
||||
typedef khint_t khiter_t;
|
||||
|
||||
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||
|
||||
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||
|
||||
#ifndef kroundup32
|
||||
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
#endif
|
||||
|
||||
#ifndef kcalloc
|
||||
#define kcalloc(N,Z) calloc(N,Z)
|
||||
#endif
|
||||
#ifndef kmalloc
|
||||
#define kmalloc(Z) malloc(Z)
|
||||
#endif
|
||||
#ifndef krealloc
|
||||
#define krealloc(P,Z) realloc(P,Z)
|
||||
#endif
|
||||
#ifndef kfree
|
||||
#define kfree(P) free(P)
|
||||
#endif
|
||||
|
||||
static const double __ac_HASH_UPPER = 0.77;
|
||||
|
||||
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
typedef struct kh_##name##_s { \
|
||||
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||
khint32_t *flags; \
|
||||
khkey_t *keys; \
|
||||
khval_t *vals; \
|
||||
} kh_##name##_t;
|
||||
|
||||
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||
extern kh_##name##_t *kh_init_##name(void); \
|
||||
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||
|
||||
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||
} \
|
||||
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h) { \
|
||||
kfree((void *)h->keys); kfree(h->flags); \
|
||||
kfree((void *)h->vals); \
|
||||
kfree(h); \
|
||||
} \
|
||||
} \
|
||||
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h && h->flags) { \
|
||||
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||
h->size = h->n_occupied = 0; \
|
||||
} \
|
||||
} \
|
||||
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||
{ \
|
||||
if (h->n_buckets) { \
|
||||
khint_t k, i, last, mask, step = 0; \
|
||||
mask = h->n_buckets - 1; \
|
||||
k = __hash_func(key); i = k & mask; \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) return h->n_buckets; \
|
||||
} \
|
||||
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||
} else return 0; \
|
||||
} \
|
||||
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||
khint32_t *new_flags = 0; \
|
||||
khint_t j = 1; \
|
||||
{ \
|
||||
kroundup32(new_n_buckets); \
|
||||
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (!new_flags) return -1; \
|
||||
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (!new_keys) { kfree(new_flags); return -1; } \
|
||||
h->keys = new_keys; \
|
||||
if (kh_is_map) { \
|
||||
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
if (!new_vals) { kfree(new_flags); return -1; } \
|
||||
h->vals = new_vals; \
|
||||
} \
|
||||
} /* otherwise shrink */ \
|
||||
} \
|
||||
} \
|
||||
if (j) { /* rehashing is needed */ \
|
||||
for (j = 0; j != h->n_buckets; ++j) { \
|
||||
if (__ac_iseither(h->flags, j) == 0) { \
|
||||
khkey_t key = h->keys[j]; \
|
||||
khval_t val; \
|
||||
khint_t new_mask; \
|
||||
new_mask = new_n_buckets - 1; \
|
||||
if (kh_is_map) val = h->vals[j]; \
|
||||
__ac_set_isdel_true(h->flags, j); \
|
||||
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||
khint_t k, i, step = 0; \
|
||||
k = __hash_func(key); \
|
||||
i = k & new_mask; \
|
||||
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
|
||||
__ac_set_isempty_false(new_flags, i); \
|
||||
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||
} else { /* write the element and jump out of the loop */ \
|
||||
h->keys[i] = key; \
|
||||
if (kh_is_map) h->vals[i] = val; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
} \
|
||||
kfree(h->flags); /* free the working space */ \
|
||||
h->flags = new_flags; \
|
||||
h->n_buckets = new_n_buckets; \
|
||||
h->n_occupied = h->size; \
|
||||
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||
{ \
|
||||
khint_t x; \
|
||||
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||
if (h->n_buckets > (h->size<<1)) { \
|
||||
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||
{ \
|
||||
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
|
||||
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||
else { \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
if (__ac_isdel(h->flags, i)) site = i; \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) { x = site; break; } \
|
||||
} \
|
||||
if (x == h->n_buckets) { \
|
||||
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||
else x = i; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; ++h->n_occupied; \
|
||||
*ret = 1; \
|
||||
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; \
|
||||
*ret = 2; \
|
||||
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||
return x; \
|
||||
} \
|
||||
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||
{ \
|
||||
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||
__ac_set_isdel_true(h->flags, x); \
|
||||
--h->size; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||
|
||||
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||
|
||||
/*! @function
|
||||
@abstract Integer hash function
|
||||
@param key The integer [khint32_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||
/*! @function
|
||||
@abstract Integer comparison function
|
||||
*/
|
||||
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract 64-bit integer hash function
|
||||
@param key The integer [khint64_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||
/*! @function
|
||||
@abstract 64-bit integer comparison function
|
||||
*/
|
||||
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract const char* hash function
|
||||
@param s Pointer to a null terminated string
|
||||
@return The hash value
|
||||
*/
|
||||
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||
{
|
||||
khint_t h = (khint_t)*s;
|
||||
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||
return h;
|
||||
}
|
||||
/*! @function
|
||||
@abstract Another interface to const char* hash function
|
||||
@param key Pointer to a null terminated string [const char*]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||
/*! @function
|
||||
@abstract Const char* comparison function
|
||||
*/
|
||||
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||
|
||||
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||
{
|
||||
key += ~(key << 15);
|
||||
key ^= (key >> 10);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 6);
|
||||
key += ~(key << 11);
|
||||
key ^= (key >> 16);
|
||||
return key;
|
||||
}
|
||||
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
|
||||
|
||||
/* --- END OF HASH FUNCTIONS --- */
|
||||
|
||||
/* Other convenient macros... */
|
||||
|
||||
/*!
|
||||
@abstract Type of the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define khash_t(name) kh_##name##_t
|
||||
|
||||
/*! @function
|
||||
@abstract Initiate a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@return Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_init(name) kh_init_##name()
|
||||
|
||||
/*! @function
|
||||
@abstract Destroy a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Reset a hash table without deallocating memory.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_clear(name, h) kh_clear_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Resize a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param s New size [khint_t]
|
||||
*/
|
||||
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||
|
||||
/*! @function
|
||||
@abstract Insert a key to the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@param r Extra return code: -1 if the operation failed;
|
||||
0 if the key is present in the hash table;
|
||||
1 if the bucket is empty (never used); 2 if the element in
|
||||
the bucket has been deleted [int*]
|
||||
@return Iterator to the inserted element [khint_t]
|
||||
*/
|
||||
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||
|
||||
/*! @function
|
||||
@abstract Retrieve a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||
*/
|
||||
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Remove a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Iterator to the element to be deleted [khint_t]
|
||||
*/
|
||||
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Test whether a bucket contains data.
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return 1 if containing data; 0 otherwise [int]
|
||||
*/
|
||||
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||
|
||||
/*! @function
|
||||
@abstract Get key given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Key [type of keys]
|
||||
*/
|
||||
#define kh_key(h, x) ((h)->keys[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get value given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Value [type of values]
|
||||
@discussion For hash sets, calling this results in segfault.
|
||||
*/
|
||||
#define kh_val(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Alias of kh_val()
|
||||
*/
|
||||
#define kh_value(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get the start iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The start iterator [khint_t]
|
||||
*/
|
||||
#define kh_begin(h) (khint_t)(0)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the end iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The end iterator [khint_t]
|
||||
*/
|
||||
#define kh_end(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of elements in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of elements in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_size(h) ((h)->size)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of buckets in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of buckets in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the entries in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param kvar Variable to which key will be assigned
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(kvar) = kh_key(h,__i); \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the values in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/* More convenient interfaces */
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT(name) \
|
||||
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT64(name) \
|
||||
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
typedef const char *kh_cstr_t;
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_STR(name) \
|
||||
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
#endif /* __AC_KHASH_H */
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fmp.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
typedef struct lfmp_t lfmp_t;
|
||||
struct lfmp_t
|
||||
{
|
||||
fmp_t fmp;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_init(lfmp_t *lfmp_,
|
||||
const uint64_t obj_size_,
|
||||
const uint64_t page_multiple_)
|
||||
{
|
||||
fmp_init(&lfmp_->fmp,obj_size_,page_multiple_);
|
||||
pthread_mutex_init(&lfmp_->lock,NULL);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_lock(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_unlock(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_slab_count(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_slab_count(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lfmp_slab_alloc(lfmp_t *lfmp_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_slab_alloc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
lfmp_alloc(lfmp_t *lfmp_)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_alloc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void*
|
||||
lfmp_calloc(lfmp_t *lfmp_)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_calloc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_free(lfmp_t *lfmp_,
|
||||
void *obj_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_free(&lfmp_->fmp,obj_);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_clear(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_clear(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_destroy(lfmp_t *lfmp_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_destroy(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
pthread_mutex_destroy(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_avail_objs(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_avail_objs(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_objs_in_slab(lfmp_t *lfmp_,
|
||||
void *slab_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_objs_in_slab(&lfmp_->fmp,slab_);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
void
|
||||
lfmp_remove_objs_in_slab(lfmp_t *lfmp_,
|
||||
void *slab_)
|
||||
{
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
fmp_remove_objs_in_slab(&lfmp_->fmp,slab_);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
lfmp_gc(lfmp_t *lfmp_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_gc(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_objs_per_slab(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_objs_per_slab(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
double
|
||||
lfmp_slab_usage_ratio(lfmp_t *lfmp_)
|
||||
{
|
||||
double rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_slab_usage_ratio(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
uint64_t
|
||||
lfmp_total_allocated_memory(lfmp_t *lfmp_)
|
||||
{
|
||||
uint64_t rv;
|
||||
|
||||
pthread_mutex_lock(&lfmp_->lock);
|
||||
rv = fmp_total_allocated_memory(&lfmp_->fmp);
|
||||
pthread_mutex_unlock(&lfmp_->lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
+206
-272
@@ -28,364 +28,298 @@
|
||||
#define FUSE_DEV_TRUNK "/dev/fuse"
|
||||
|
||||
enum {
|
||||
KEY_ALLOW_ROOT,
|
||||
KEY_RO,
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
KEY_KERN
|
||||
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 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 },
|
||||
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);
|
||||
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_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.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");
|
||||
|
||||
+431
-464
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,
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include "queue.hpp"
|
||||
|
||||
|
||||
|
||||
class simple_thread_pool
|
||||
{
|
||||
public:
|
||||
explicit simple_thread_pool(std::size_t thread_count = std::thread::hardware_concurrency())
|
||||
{
|
||||
if(!thread_count)
|
||||
throw std::invalid_argument("bad thread count! must be non-zero!");
|
||||
|
||||
auto worker = [this]()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
proc_t f;
|
||||
if(!m_queue.pop(f))
|
||||
break;
|
||||
f();
|
||||
}
|
||||
};
|
||||
|
||||
m_threads.reserve(thread_count);
|
||||
while(thread_count--)
|
||||
m_threads.emplace_back(worker);
|
||||
}
|
||||
|
||||
~simple_thread_pool()
|
||||
{
|
||||
m_queue.unblock();
|
||||
for(auto& thread : m_threads)
|
||||
thread.join();
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
void enqueue_work(F&& f, Args&&... args)
|
||||
{
|
||||
m_queue.push([p = std::forward<F>(f), t = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(p, t); });
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
[[nodiscard]] auto enqueue_task(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>>
|
||||
{
|
||||
using task_return_type = std::invoke_result_t<F, Args...>;
|
||||
using task_type = std::packaged_task<task_return_type()>;
|
||||
|
||||
auto task = std::make_shared<task_type>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
|
||||
auto result = task->get_future();
|
||||
|
||||
m_queue.push([=]() { (*task)(); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
using proc_t = std::function<void(void)>;
|
||||
using queue_t = unbounded_queue<proc_t>;
|
||||
queue_t m_queue;
|
||||
|
||||
using threads_t = std::vector<std::thread>;
|
||||
threads_t m_threads;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class thread_pool
|
||||
{
|
||||
public:
|
||||
explicit thread_pool(std::size_t thread_count = std::thread::hardware_concurrency())
|
||||
: m_queues(thread_count), m_count(thread_count)
|
||||
{
|
||||
if(!thread_count)
|
||||
throw std::invalid_argument("bad thread count! must be non-zero!");
|
||||
|
||||
auto worker = [this](auto i)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
proc_t f;
|
||||
for(std::size_t n = 0; n < m_count * K; ++n)
|
||||
if(m_queues[(i + n) % m_count].try_pop(f))
|
||||
break;
|
||||
if(!f && !m_queues[i].pop(f))
|
||||
break;
|
||||
f();
|
||||
}
|
||||
};
|
||||
|
||||
m_threads.reserve(thread_count);
|
||||
for(std::size_t i = 0; i < thread_count; ++i)
|
||||
m_threads.emplace_back(worker, i);
|
||||
}
|
||||
|
||||
~thread_pool()
|
||||
{
|
||||
for(auto& queue : m_queues)
|
||||
queue.unblock();
|
||||
for(auto& thread : m_threads)
|
||||
thread.join();
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
void enqueue_work(F&& f, Args&&... args)
|
||||
{
|
||||
auto work = [p = std::forward<F>(f), t = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(p, t); };
|
||||
auto i = m_index++;
|
||||
|
||||
for(std::size_t n = 0; n < m_count * K; ++n)
|
||||
if(m_queues[(i + n) % m_count].try_push(work))
|
||||
return;
|
||||
|
||||
m_queues[i % m_count].push(std::move(work));
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
[[nodiscard]] auto enqueue_task(F&& f, Args&&... args) -> std::future<std::invoke_result_t<F, Args...>>
|
||||
{
|
||||
using task_return_type = std::invoke_result_t<F, Args...>;
|
||||
using task_type = std::packaged_task<task_return_type()>;
|
||||
|
||||
auto task = std::make_shared<task_type>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
|
||||
auto work = [=]() { (*task)(); };
|
||||
auto result = task->get_future();
|
||||
auto i = m_index++;
|
||||
|
||||
for(auto n = 0; n < m_count * K; ++n)
|
||||
if(m_queues[(i + n) % m_count].try_push(work))
|
||||
return result;
|
||||
|
||||
m_queues[i % m_count].push(std::move(work));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
using proc_t = std::function<void(void)>;
|
||||
using queue_t = unbounded_queue<proc_t>;
|
||||
using queues_t = std::vector<queue_t>;
|
||||
queues_t m_queues;
|
||||
|
||||
using threads_t = std::vector<std::thread>;
|
||||
threads_t m_threads;
|
||||
|
||||
const std::size_t m_count;
|
||||
std::atomic_uint m_index = 0;
|
||||
|
||||
inline static const unsigned int K = 2;
|
||||
};
|
||||
@@ -0,0 +1,256 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class unbounded_queue
|
||||
{
|
||||
public:
|
||||
explicit unbounded_queue(bool block = true)
|
||||
: m_block{ block } {}
|
||||
|
||||
void push(const T& item)
|
||||
{
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_queue.push(item);
|
||||
}
|
||||
m_condition.notify_one();
|
||||
}
|
||||
|
||||
void push(T&& item)
|
||||
{
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_queue.push(std::move(item));
|
||||
}
|
||||
m_condition.notify_one();
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void emplace(Args&&... args)
|
||||
{
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_queue.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
m_condition.notify_one();
|
||||
}
|
||||
|
||||
bool try_push(const T& item)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(m_queue_lock, std::try_to_lock);
|
||||
if(!lock)
|
||||
return false;
|
||||
m_queue.push(item);
|
||||
}
|
||||
m_condition.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_push(T&& item)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(m_queue_lock, std::try_to_lock);
|
||||
if(!lock)
|
||||
return false;
|
||||
m_queue.push(std::move(item));
|
||||
}
|
||||
m_condition.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop(T& item)
|
||||
{
|
||||
std::unique_lock guard(m_queue_lock);
|
||||
m_condition.wait(guard, [&]() { return !m_queue.empty() || !m_block; });
|
||||
if(m_queue.empty())
|
||||
return false;
|
||||
item = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_pop(T& item)
|
||||
{
|
||||
std::unique_lock lock(m_queue_lock, std::try_to_lock);
|
||||
if(!lock || m_queue.empty())
|
||||
return false;
|
||||
item = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
void block()
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_block = true;
|
||||
}
|
||||
|
||||
void unblock()
|
||||
{
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_block = false;
|
||||
}
|
||||
m_condition.notify_all();
|
||||
}
|
||||
|
||||
bool blocking() const
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
return m_block;
|
||||
}
|
||||
|
||||
private:
|
||||
using queue_t = std::queue<T>;
|
||||
queue_t m_queue;
|
||||
|
||||
bool m_block;
|
||||
|
||||
mutable std::mutex m_queue_lock;
|
||||
std::condition_variable m_condition;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class bounded_queue
|
||||
{
|
||||
public:
|
||||
explicit bounded_queue(std::size_t max_size, bool block = true)
|
||||
: m_block{ block }, m_max_size{ max_size }
|
||||
{
|
||||
if(!m_max_size)
|
||||
throw std::invalid_argument("bad queue max-size! must be non-zero!");
|
||||
}
|
||||
|
||||
bool push(const T& item)
|
||||
{
|
||||
{
|
||||
std::unique_lock guard(m_queue_lock);
|
||||
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
|
||||
if(m_queue.size() == m_max_size)
|
||||
return false;
|
||||
m_queue.push(item);
|
||||
}
|
||||
m_condition_pop.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push(T&& item)
|
||||
{
|
||||
{
|
||||
std::unique_lock guard(m_queue_lock);
|
||||
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
|
||||
if(m_queue.size() == m_max_size)
|
||||
return false;
|
||||
m_queue.push(std::move(item));
|
||||
}
|
||||
m_condition_pop.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
bool emplace(Args&&... args)
|
||||
{
|
||||
{
|
||||
std::unique_lock guard(m_queue_lock);
|
||||
m_condition_push.wait(guard, [&]() { return m_queue.size() < m_max_size || !m_block; });
|
||||
if(m_queue.size() == m_max_size)
|
||||
return false;
|
||||
m_queue.emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
m_condition_pop.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop(T& item)
|
||||
{
|
||||
{
|
||||
std::unique_lock guard(m_queue_lock);
|
||||
m_condition_pop.wait(guard, [&]() { return !m_queue.empty() || !m_block; });
|
||||
if(m_queue.empty())
|
||||
return false;
|
||||
item = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
}
|
||||
m_condition_push.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
return m_queue.size();
|
||||
}
|
||||
|
||||
std::size_t capacity() const
|
||||
{
|
||||
return m_max_size;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
return m_queue.empty();
|
||||
}
|
||||
|
||||
bool full() const
|
||||
{
|
||||
std::scoped_lock lock(m_queue_lock);
|
||||
return m_queue.size() == capacity();
|
||||
}
|
||||
|
||||
void block()
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_block = true;
|
||||
}
|
||||
|
||||
void unblock()
|
||||
{
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
m_block = false;
|
||||
}
|
||||
m_condition_push.notify_all();
|
||||
m_condition_pop.notify_all();
|
||||
}
|
||||
|
||||
bool blocking() const
|
||||
{
|
||||
std::scoped_lock guard(m_queue_lock);
|
||||
return m_block;
|
||||
}
|
||||
|
||||
private:
|
||||
using queue_t = std::queue<T>;
|
||||
queue_t m_queue;
|
||||
|
||||
bool m_block;
|
||||
const std::size_t m_max_size;
|
||||
|
||||
mutable std::mutex m_queue_lock;
|
||||
std::condition_variable m_condition_push;
|
||||
std::condition_variable m_condition_pop;
|
||||
};
|
||||
@@ -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
|
||||
@@ -782,9 +782,7 @@ static int do_mount(const char *mnt, char **typep, mode_t rootmode,
|
||||
skip_option = 1;
|
||||
}
|
||||
}
|
||||
if (getuid() != 0 && !user_allow_other &&
|
||||
(opt_eq(s, len, "allow_other") ||
|
||||
opt_eq(s, len, "allow_root"))) {
|
||||
if (getuid() != 0 && !user_allow_other && opt_eq(s, len, "allow_other")) {
|
||||
fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
+1168
-991
File diff suppressed because it is too large
Load Diff
+59
-277
@@ -18,298 +18,80 @@
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "fs_glob.hpp"
|
||||
#include "str.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "num.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
Branch::Branch(const uint64_t &default_minfreespace_)
|
||||
: _default_minfreespace(&default_minfreespace_)
|
||||
{
|
||||
}
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
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));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
pthread_rwlock_init(&lock,NULL);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse(const string &str_,
|
||||
Branches &branches_)
|
||||
{
|
||||
string str;
|
||||
Branch branch;
|
||||
vector<string> globbed;
|
||||
|
||||
str = str_;
|
||||
branch.mode = Branch::INVALID;
|
||||
if(str::endswith(str,"=RO"))
|
||||
branch.mode = Branch::RO;
|
||||
ef(str::endswith(str,"=RW"))
|
||||
branch.mode = Branch::RW;
|
||||
ef(str::endswith(str,"=NC"))
|
||||
branch.mode = Branch::NC;
|
||||
|
||||
if(branch.mode != Branch::INVALID)
|
||||
str.resize(str.size() - 3);
|
||||
else
|
||||
branch.mode = Branch::RW;
|
||||
|
||||
fs::glob(str,globbed);
|
||||
fs::realpathize(globbed);
|
||||
for(size_t i = 0; i < globbed.size(); i++)
|
||||
{
|
||||
branch.path = globbed[i];
|
||||
branches_.push_back(branch);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
set(Branches &branches_,
|
||||
const std::string &str_)
|
||||
{
|
||||
vector<string> paths;
|
||||
|
||||
branches_.clear();
|
||||
|
||||
str::split(paths,str_,':');
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
Branches tmp;
|
||||
|
||||
parse(paths[i],tmp);
|
||||
|
||||
branches_.insert(branches_.end(),
|
||||
tmp.begin(),
|
||||
tmp.end());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
add_begin(Branches &branches_,
|
||||
const std::string &str_)
|
||||
{
|
||||
vector<string> paths;
|
||||
|
||||
str::split(paths,str_,':');
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
Branches tmp;
|
||||
|
||||
parse(paths[i],tmp);
|
||||
|
||||
branches_.insert(branches_.begin(),
|
||||
tmp.begin(),
|
||||
tmp.end());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
add_end(Branches &branches_,
|
||||
const std::string &str_)
|
||||
{
|
||||
vector<string> paths;
|
||||
|
||||
str::split(paths,str_,':');
|
||||
|
||||
for(size_t i = 0; i < paths.size(); i++)
|
||||
{
|
||||
Branches tmp;
|
||||
|
||||
parse(paths[i],tmp);
|
||||
|
||||
branches_.insert(branches_.end(),
|
||||
tmp.begin(),
|
||||
tmp.end());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
erase_begin(Branches &branches_)
|
||||
{
|
||||
branches_.erase(branches_.begin());
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
erase_end(Branches &branches_)
|
||||
{
|
||||
branches_.pop_back();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
erase_fnmatch(Branches &branches_,
|
||||
const std::string &str_)
|
||||
{
|
||||
vector<string> patterns;
|
||||
|
||||
str::split(patterns,str_,':');
|
||||
|
||||
for(Branches::iterator i = branches_.begin();
|
||||
i != branches_.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_.erase(i) : (i+1));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Branches::from_string(const std::string &s_)
|
||||
{
|
||||
rwlock::WriteGuard guard(&lock);
|
||||
std::string instr;
|
||||
std::string values;
|
||||
|
||||
::split(s_,&instr,&values);
|
||||
|
||||
if(instr == "+")
|
||||
::add_end(*this,values);
|
||||
ef(instr == "+<")
|
||||
::add_begin(*this,values);
|
||||
ef(instr == "+>")
|
||||
::add_end(*this,values);
|
||||
ef(instr == "-")
|
||||
::erase_fnmatch(*this,values);
|
||||
ef(instr == "-<")
|
||||
::erase_begin(*this);
|
||||
ef(instr == "->")
|
||||
::erase_end(*this);
|
||||
ef(instr == "=")
|
||||
::set(*this,values);
|
||||
ef(instr.empty())
|
||||
::set(*this,values);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string
|
||||
Branches::to_string(void) const
|
||||
{
|
||||
rwlock::ReadGuard guard(&lock);
|
||||
string tmp;
|
||||
|
||||
for(size_t i = 0; i < size(); i++)
|
||||
{
|
||||
const Branch &branch = (*this)[i];
|
||||
|
||||
tmp += branch.path;
|
||||
|
||||
tmp += '=';
|
||||
switch(branch.mode)
|
||||
{
|
||||
default:
|
||||
case Branch::RW:
|
||||
tmp += "RW";
|
||||
break;
|
||||
case Branch::RO:
|
||||
tmp += "RO";
|
||||
break;
|
||||
case Branch::NC:
|
||||
tmp += "NC";
|
||||
break;
|
||||
}
|
||||
|
||||
tmp += ':';
|
||||
}
|
||||
|
||||
if(*tmp.rbegin() == ':')
|
||||
tmp.erase(tmp.size() - 1);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void
|
||||
Branches::to_paths(vector<string> &vec_) const
|
||||
{
|
||||
rwlock::ReadGuard guard(&lock);
|
||||
|
||||
for(size_t i = 0; i < size(); i++)
|
||||
{
|
||||
const Branch &branch = (*this)[i];
|
||||
|
||||
vec_.push_back(branch.path);
|
||||
}
|
||||
}
|
||||
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
: _branches(b_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
SrcMounts::from_string(const std::string &s_)
|
||||
{
|
||||
return _branches.from_string(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
SrcMounts::to_string(void) const
|
||||
{
|
||||
rwlock::ReadGuard guard(&_branches.lock);
|
||||
std::string rv;
|
||||
|
||||
for(uint64_t i = 0; i < _branches.size(); i++)
|
||||
{
|
||||
rv += _branches[i].path;
|
||||
rv += ':';
|
||||
}
|
||||
|
||||
if(*rv.rbegin() == ':')
|
||||
rv.erase(rv.size() - 1);
|
||||
|
||||
return rv;
|
||||
return ((mode == Branch::Mode::RO) ||
|
||||
(mode == Branch::Mode::NC));
|
||||
}
|
||||
|
||||
+24
-32
@@ -18,58 +18,50 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rwlock.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
class Branch
|
||||
class Branch final : public ToFromString
|
||||
{
|
||||
public:
|
||||
enum Mode
|
||||
typedef std::vector<Branch> Vector;
|
||||
|
||||
public:
|
||||
Branch(const uint64_t &default_minfreespace_);
|
||||
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
INVALID,
|
||||
RO,
|
||||
RW,
|
||||
NC
|
||||
INVALID,
|
||||
RO,
|
||||
RW,
|
||||
NC
|
||||
};
|
||||
|
||||
Mode mode;
|
||||
std::string path;
|
||||
|
||||
public:
|
||||
bool ro(void) const;
|
||||
bool nc(void) const;
|
||||
bool ro_or_nc(void) const;
|
||||
};
|
||||
|
||||
class Branches : public std::vector<Branch>, public ToFromString
|
||||
{
|
||||
public:
|
||||
Branches();
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str);
|
||||
std::string to_string(void) const;
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
void to_paths(std::vector<std::string> &vec) const;
|
||||
uint64_t minfreespace() const;
|
||||
void set_minfreespace(const uint64_t);
|
||||
|
||||
public:
|
||||
mutable pthread_rwlock_t lock;
|
||||
};
|
||||
|
||||
class SrcMounts : public ToFromString
|
||||
{
|
||||
public:
|
||||
SrcMounts(Branches &b_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str);
|
||||
std::string to_string(void) const;
|
||||
Mode mode;
|
||||
std::string path;
|
||||
|
||||
private:
|
||||
Branches &_branches;
|
||||
nonstd::optional<uint64_t> _minfreespace;
|
||||
const uint64_t *_default_minfreespace;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "branches.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs_glob.hpp"
|
||||
#include "fs_realpathize.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "num.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using nonstd::optional;
|
||||
|
||||
|
||||
Branches::Impl::Impl(const uint64_t &default_minfreespace_)
|
||||
: _default_minfreespace(default_minfreespace_)
|
||||
{
|
||||
}
|
||||
|
||||
Branches::Impl&
|
||||
Branches::Impl::operator=(Branches::Impl &rval_)
|
||||
{
|
||||
auto this_base = dynamic_cast<Branch::Vector*>(this);
|
||||
auto rval_base = dynamic_cast<Branch::Vector*>(&rval_);
|
||||
|
||||
*this_base = *rval_base;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Branches::Impl&
|
||||
Branches::Impl::operator=(Branches::Impl &&rval_)
|
||||
{
|
||||
auto this_base = dynamic_cast<Branch::Vector*>(this);
|
||||
auto rval_base = dynamic_cast<Branch::Vector*>(&rval_);
|
||||
|
||||
*this_base = std::move(*rval_base);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const
|
||||
uint64_t&
|
||||
Branches::Impl::minfreespace(void) const
|
||||
{
|
||||
return _default_minfreespace;
|
||||
}
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
void
|
||||
split(const std::string &s_,
|
||||
std::string *instr_,
|
||||
std::string *values_)
|
||||
{
|
||||
uint64_t offset;
|
||||
|
||||
offset = s_.find_first_not_of("+<>-=");
|
||||
if(offset > 1)
|
||||
offset = 2;
|
||||
*instr_ = s_.substr(0,offset);
|
||||
if(offset != std::string::npos)
|
||||
*values_ = s_.substr(offset);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_mode(const string &str_,
|
||||
Branch::Mode *mode_)
|
||||
{
|
||||
if(str_ == "RW")
|
||||
*mode_ = Branch::Mode::RW;
|
||||
ef(str_ == "RO")
|
||||
*mode_ = Branch::Mode::RO;
|
||||
ef(str_ == "NC")
|
||||
*mode_ = Branch::Mode::NC;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_minfreespace(const string &str_,
|
||||
optional<uint64_t> *minfreespace_)
|
||||
{
|
||||
int rv;
|
||||
uint64_t uint64;
|
||||
|
||||
rv = str::from(str_,&uint64);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
*minfreespace_ = uint64;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_branch(const string &str_,
|
||||
string *glob_,
|
||||
Branch::Mode *mode_,
|
||||
optional<uint64_t> *minfreespace_)
|
||||
{
|
||||
int rv;
|
||||
string options;
|
||||
vector<string> v;
|
||||
|
||||
str::rsplit1(str_,'=',&v);
|
||||
switch(v.size())
|
||||
{
|
||||
case 1:
|
||||
*glob_ = v[0];
|
||||
*mode_ = Branch::Mode::RW;
|
||||
break;
|
||||
case 2:
|
||||
*glob_ = v[0];
|
||||
options = v[1];
|
||||
v.clear();
|
||||
str::split(options,',',&v);
|
||||
switch(v.size())
|
||||
{
|
||||
case 2:
|
||||
rv = l::parse_minfreespace(v[1],minfreespace_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
case 1:
|
||||
rv = l::parse_mode(v[0],mode_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
break;
|
||||
case 0:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse(const string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
string glob;
|
||||
StrVec paths;
|
||||
optional<uint64_t> minfreespace;
|
||||
Branch branch(branches_->minfreespace());
|
||||
|
||||
rv = l::parse_branch(str_,&glob,&branch.mode,&minfreespace);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
if(minfreespace.has_value())
|
||||
branch.set_minfreespace(minfreespace.value());
|
||||
|
||||
fs::glob(glob,&paths);
|
||||
fs::realpathize(&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
branch.path = path;
|
||||
branches_->push_back(branch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
set(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
StrVec paths;
|
||||
Branches::Impl tmp_branches(branches_->minfreespace());
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
rv = l::parse(path,&tmp_branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
*branches_ = std::move(tmp_branches);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
add_begin(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
vector<string> paths;
|
||||
Branches::Impl tmp_branches(branches_->minfreespace());
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
rv = l::parse(path,&tmp_branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->insert(branches_->begin(),
|
||||
tmp_branches.begin(),
|
||||
tmp_branches.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
add_end(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
int rv;
|
||||
StrVec paths;
|
||||
Branches::Impl tmp_branches(branches_->minfreespace());
|
||||
|
||||
str::split(str_,':',&paths);
|
||||
for(auto &path : paths)
|
||||
{
|
||||
rv = l::parse(path,&tmp_branches);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
branches_->insert(branches_->end(),
|
||||
tmp_branches.begin(),
|
||||
tmp_branches.end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_begin(Branches::Impl *branches_)
|
||||
{
|
||||
branches_->erase(branches_->begin());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_end(Branches::Impl *branches_)
|
||||
{
|
||||
branches_->pop_back();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
erase_fnmatch(const std::string &str_,
|
||||
Branches::Impl *branches_)
|
||||
{
|
||||
StrVec patterns;
|
||||
|
||||
str::split(str_,':',&patterns);
|
||||
for(auto i = branches_->begin(); i != branches_->end();)
|
||||
{
|
||||
int match = FNM_NOMATCH;
|
||||
|
||||
for(auto pi = patterns.cbegin(); pi != patterns.cend() && match != 0; ++pi)
|
||||
{
|
||||
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
|
||||
}
|
||||
|
||||
i = ((match == 0) ? branches_->erase(i) : (i+1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Branches::Impl::from_string(const std::string &s_)
|
||||
{
|
||||
std::string instr;
|
||||
std::string values;
|
||||
|
||||
l::split(s_,&instr,&values);
|
||||
|
||||
if(instr == "+")
|
||||
return l::add_end(values,this);
|
||||
if(instr == "+<")
|
||||
return l::add_begin(values,this);
|
||||
if(instr == "+>")
|
||||
return l::add_end(values,this);
|
||||
if(instr == "-")
|
||||
return l::erase_fnmatch(values,this);
|
||||
if(instr == "-<")
|
||||
return l::erase_begin(this);
|
||||
if(instr == "->")
|
||||
return l::erase_end(this);
|
||||
if(instr == "=")
|
||||
return l::set(values,this);
|
||||
if(instr.empty())
|
||||
return l::set(values,this);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
std::string
|
||||
Branches::Impl::to_string(void) const
|
||||
{
|
||||
string tmp;
|
||||
|
||||
if(empty())
|
||||
return tmp;
|
||||
|
||||
for(auto &branch : *this)
|
||||
{
|
||||
tmp += branch.to_string();
|
||||
tmp += ':';
|
||||
}
|
||||
|
||||
tmp.pop_back();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void
|
||||
Branches::Impl::to_paths(StrVec &paths_) const
|
||||
{
|
||||
for(auto &branch : *this)
|
||||
{
|
||||
paths_.push_back(branch.path);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Branches::from_string(const std::string &str_)
|
||||
{
|
||||
int rv;
|
||||
Branches::Ptr impl;
|
||||
Branches::Ptr new_impl;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(_mutex);
|
||||
impl = _impl;
|
||||
}
|
||||
|
||||
new_impl = std::make_shared<Branches::Impl>(impl->minfreespace());
|
||||
*new_impl = *impl;
|
||||
|
||||
rv = new_impl->from_string(str_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(_mutex);
|
||||
_impl = new_impl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string
|
||||
Branches::to_string(void) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(_mutex);
|
||||
|
||||
return _impl->to_string();
|
||||
}
|
||||
|
||||
SrcMounts::SrcMounts(Branches &b_)
|
||||
: _branches(b_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
SrcMounts::from_string(const std::string &s_)
|
||||
{
|
||||
return _branches.from_string(s_);
|
||||
}
|
||||
|
||||
std::string
|
||||
SrcMounts::to_string(void) const
|
||||
{
|
||||
std::string rv;
|
||||
Branches::CPtr branches = _branches;
|
||||
|
||||
if(branches->empty())
|
||||
return rv;
|
||||
|
||||
for(const auto &branch : *branches)
|
||||
{
|
||||
rv += branch.path;
|
||||
rv += ':';
|
||||
}
|
||||
|
||||
rv.pop_back();
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "nonstd/optional.hpp"
|
||||
#include "strvec.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class Branches final : public ToFromString
|
||||
{
|
||||
public:
|
||||
class Impl final : public ToFromString, public Branch::Vector
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<Impl> Ptr;
|
||||
typedef std::shared_ptr<const Impl> CPtr;
|
||||
|
||||
public:
|
||||
Impl(const uint64_t &default_minfreespace_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
const uint64_t& minfreespace(void) const;
|
||||
void to_paths(StrVec &strvec) const;
|
||||
|
||||
public:
|
||||
Impl& operator=(Impl &impl_);
|
||||
Impl& operator=(Impl &&impl_);
|
||||
|
||||
private:
|
||||
const uint64_t &_default_minfreespace;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef Branches::Impl::Ptr Ptr;
|
||||
typedef Branches::Impl::CPtr CPtr;
|
||||
|
||||
public:
|
||||
Branches(const uint64_t &default_minfreespace_)
|
||||
: _impl(std::make_shared<Impl>(default_minfreespace_))
|
||||
{}
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
public:
|
||||
operator CPtr() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
CPtr operator->() const { std::lock_guard<std::mutex> lg(_mutex); return _impl; }
|
||||
|
||||
private:
|
||||
mutable std::mutex _mutex;
|
||||
Ptr _impl;
|
||||
};
|
||||
|
||||
class SrcMounts : public ToFromString
|
||||
{
|
||||
public:
|
||||
SrcMounts(Branches &b_);
|
||||
|
||||
public:
|
||||
int from_string(const std::string &str) final;
|
||||
std::string to_string(void) const final;
|
||||
|
||||
private:
|
||||
Branches &_branches;
|
||||
};
|
||||
+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;
|
||||
}
|
||||
|
||||
|
||||
+22
-32
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
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
|
||||
@@ -14,46 +16,34 @@
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "category.hpp"
|
||||
#include "buildvector.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
#define CATEGORY(X) Category(Category::Enum::X,#X)
|
||||
#include <string>
|
||||
|
||||
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)
|
||||
int
|
||||
Category::Base::from_string(const std::string &s_)
|
||||
{
|
||||
for(int i = Enum::BEGIN; i != Enum::END; ++i)
|
||||
int rv;
|
||||
|
||||
for(auto func : funcs)
|
||||
{
|
||||
if(categories[i] == str)
|
||||
return categories[i];
|
||||
rv = func->from_string(s_);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
}
|
||||
|
||||
return invalid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Category&
|
||||
Category::find(const Category::Enum::Type i)
|
||||
std::string
|
||||
Category::Base::to_string(void) const
|
||||
{
|
||||
if(i >= Category::Enum::BEGIN &&
|
||||
i < Category::Enum::END)
|
||||
return categories[i];
|
||||
std::set<std::string> rv;
|
||||
|
||||
return invalid;
|
||||
for(const auto func : funcs)
|
||||
rv.insert(func->to_string());
|
||||
|
||||
return str::join(rv,',');
|
||||
}
|
||||
|
||||
+79
-56
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -16,69 +16,92 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "tofrom_string.hpp"
|
||||
#include "funcs.hpp"
|
||||
#include "func.hpp"
|
||||
|
||||
class Category
|
||||
#include <string>
|
||||
|
||||
namespace Category
|
||||
{
|
||||
public:
|
||||
struct Enum
|
||||
class Base : public ToFromString
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
invalid = -1,
|
||||
BEGIN = 0,
|
||||
action = BEGIN,
|
||||
create,
|
||||
search,
|
||||
END
|
||||
};
|
||||
public:
|
||||
int from_string(const std::string &s) final;
|
||||
std::string to_string() const final;
|
||||
|
||||
protected:
|
||||
std::vector<ToFromString*> funcs;
|
||||
};
|
||||
|
||||
class Action final : public Base
|
||||
{
|
||||
private:
|
||||
Action();
|
||||
|
||||
public:
|
||||
Action(Funcs &funcs_)
|
||||
{
|
||||
funcs.push_back(&funcs_.chmod);
|
||||
funcs.push_back(&funcs_.chown);
|
||||
funcs.push_back(&funcs_.link);
|
||||
funcs.push_back(&funcs_.removexattr);
|
||||
funcs.push_back(&funcs_.rename);
|
||||
funcs.push_back(&funcs_.rmdir);
|
||||
funcs.push_back(&funcs_.setxattr);
|
||||
funcs.push_back(&funcs_.truncate);
|
||||
funcs.push_back(&funcs_.unlink);
|
||||
funcs.push_back(&funcs_.utimens);
|
||||
}
|
||||
};
|
||||
|
||||
class Create final : public Base
|
||||
{
|
||||
private:
|
||||
Create();
|
||||
|
||||
public:
|
||||
Create(Funcs &funcs_)
|
||||
{
|
||||
funcs.push_back(&funcs_.create);
|
||||
funcs.push_back(&funcs_.mkdir);
|
||||
funcs.push_back(&funcs_.mknod);
|
||||
funcs.push_back(&funcs_.symlink);
|
||||
}
|
||||
};
|
||||
|
||||
class Search final : public Base
|
||||
{
|
||||
private:
|
||||
Search();
|
||||
|
||||
public:
|
||||
Search(Funcs &funcs_)
|
||||
{
|
||||
funcs.push_back(&funcs_.access);
|
||||
funcs.push_back(&funcs_.getattr);
|
||||
funcs.push_back(&funcs_.getxattr);
|
||||
funcs.push_back(&funcs_.listxattr);
|
||||
funcs.push_back(&funcs_.open);
|
||||
funcs.push_back(&funcs_.readlink);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Categories final
|
||||
{
|
||||
private:
|
||||
Enum::Type _enum;
|
||||
std::string _str;
|
||||
Categories();
|
||||
|
||||
public:
|
||||
Category()
|
||||
: _enum(invalid),
|
||||
_str(invalid)
|
||||
{
|
||||
}
|
||||
|
||||
Category(const Enum::Type enum_,
|
||||
const std::string &str_)
|
||||
: _enum(enum_),
|
||||
_str(str_)
|
||||
{
|
||||
}
|
||||
Categories(Funcs &funcs_)
|
||||
: action(funcs_),
|
||||
create(funcs_),
|
||||
search(funcs_)
|
||||
{}
|
||||
|
||||
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;
|
||||
Category::Action action;
|
||||
Category::Create create;
|
||||
Category::Search search;
|
||||
};
|
||||
|
||||
+181
-56
@@ -18,17 +18,20 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "fs.hpp"
|
||||
#include "num.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "str.hpp"
|
||||
#include "to_string.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -38,6 +41,11 @@ using std::string;
|
||||
|
||||
#define IFERT(S) if(S == s_) return true
|
||||
|
||||
const std::string CONTROLFILE = "/.mergerfs";
|
||||
|
||||
Config Config::_singleton;
|
||||
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
@@ -61,49 +69,48 @@ namespace l
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
:
|
||||
open_cache(),
|
||||
|
||||
controlfile("/.mergerfs"),
|
||||
|
||||
async_read(true),
|
||||
auto_cache(false),
|
||||
branches(),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::LIBFUSE),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
category(func),
|
||||
direct_io(false),
|
||||
dropcacheonclose(false),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size(FUSE_MAX_MAX_PAGES),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
link_cow(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
mount(),
|
||||
moveonenospc(false),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
nullrw(false),
|
||||
pid(::getpid()),
|
||||
posix_acl(false),
|
||||
readdir(ReadDir::ENUM::POSIX),
|
||||
readdirplus(false),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
threads(0),
|
||||
version(MERGERFS_VERSION),
|
||||
writeback_cache(false),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH)
|
||||
: async_read(true),
|
||||
auto_cache(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
branches(minfreespace),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::LIBFUSE),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
category(func),
|
||||
direct_io(false),
|
||||
dropcacheonclose(false),
|
||||
fsname(),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
func(),
|
||||
fuse_msg_size(FUSE_MAX_MAX_PAGES),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
link_cow(false),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
log_metrics(false),
|
||||
mount(),
|
||||
moveonenospc(false),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
nullrw(false),
|
||||
pid(::getpid()),
|
||||
posix_acl(false),
|
||||
readdir(ReadDir::ENUM::POSIX),
|
||||
readdirplus(false),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
threads(0),
|
||||
version(MERGERFS_VERSION),
|
||||
writeback_cache(false),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH)
|
||||
{
|
||||
_map["async_read"] = &async_read;
|
||||
_map["auto_cache"] = &auto_cache;
|
||||
@@ -121,6 +128,7 @@ Config::Config()
|
||||
_map["category.search"] = &category.search;
|
||||
_map["direct_io"] = &direct_io;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_map["fsname"] = &fsname;
|
||||
_map["func.access"] = &func.access;
|
||||
_map["func.chmod"] = &func.chmod;
|
||||
@@ -147,6 +155,8 @@ Config::Config()
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel_cache"] = &kernel_cache;
|
||||
_map["link_cow"] = &link_cow;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["log.metrics"] = &log_metrics;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &mount;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
@@ -156,6 +166,7 @@ Config::Config()
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
// _map["readdir"] = &readdir;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["security_capability"] = &security_capability;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
@@ -167,17 +178,22 @@ Config::Config()
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
const
|
||||
Config&
|
||||
Config::ro(void)
|
||||
Config::operator=(const Config &cfg_)
|
||||
{
|
||||
return *((Config*)fuse_get_context()->private_data);
|
||||
}
|
||||
int rv;
|
||||
std::string val;
|
||||
|
||||
Config&
|
||||
Config::rw(void)
|
||||
{
|
||||
return *((Config*)fuse_get_context()->private_data);
|
||||
for(auto &kv : _map)
|
||||
{
|
||||
rv = cfg_.get(kv.first,&val);
|
||||
if(rv)
|
||||
continue;
|
||||
|
||||
kv.second->from_string(val);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -255,11 +271,80 @@ Config::set(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
if(l::readonly(key_))
|
||||
return -EINVAL;
|
||||
return -EROFS;
|
||||
|
||||
return set_raw(key_,value_);
|
||||
}
|
||||
|
||||
int
|
||||
Config::set(const std::string &kv_)
|
||||
{
|
||||
std::string key;
|
||||
std::string val;
|
||||
|
||||
str::splitkv(kv_,'=',&key,&val);
|
||||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
return set(key,val);
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_stream(std::istream &istrm_,
|
||||
ErrVec *errs_)
|
||||
{
|
||||
int rv;
|
||||
std::string line;
|
||||
std::string key;
|
||||
std::string val;
|
||||
Config newcfg;
|
||||
|
||||
newcfg = *this;
|
||||
|
||||
while(std::getline(istrm_,line,'\n'))
|
||||
{
|
||||
line = str::trim(line);
|
||||
if(!line.empty() && (line[0] == '#'))
|
||||
continue;
|
||||
|
||||
str::splitkv(line,'=',&key,&val);
|
||||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
rv = newcfg.set(key,val);
|
||||
if(rv < 0)
|
||||
errs_->push_back({rv,key});
|
||||
}
|
||||
|
||||
if(!errs_->empty())
|
||||
return -EINVAL;
|
||||
|
||||
*this = newcfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_file(const std::string &filepath_,
|
||||
ErrVec *errs_)
|
||||
{
|
||||
int rv;
|
||||
std::ifstream ifstrm;
|
||||
|
||||
ifstrm.open(filepath_);
|
||||
if(!ifstrm.good())
|
||||
{
|
||||
errs_->push_back({-errno,filepath_});
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rv = from_stream(ifstrm,errs_);
|
||||
|
||||
ifstrm.close();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config &c_)
|
||||
@@ -269,7 +354,47 @@ operator<<(std::ostream &os_,
|
||||
|
||||
for(i = c_._map.begin(), ei = c_._map.end(); i != ei; ++i)
|
||||
{
|
||||
os_ << i->first << '=' << i->second << '\n';
|
||||
os_ << i->first << '=' << i->second->to_string() << std::endl;
|
||||
}
|
||||
|
||||
return os_;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
std::string
|
||||
err2str(const int err_)
|
||||
{
|
||||
switch(err_)
|
||||
{
|
||||
case 0:
|
||||
return std::string();
|
||||
case -EINVAL:
|
||||
return "invalid value";
|
||||
case -ENOATTR:
|
||||
return "unknown option";
|
||||
case -EROFS:
|
||||
return "read-only option";
|
||||
default:
|
||||
return strerror(-err_);
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config::ErrVec &ev_)
|
||||
{
|
||||
std::string errstr;
|
||||
|
||||
for(auto &err : ev_)
|
||||
{
|
||||
os_ << "* ERROR: ";
|
||||
errstr = err2str(err.err);
|
||||
if(!errstr.empty())
|
||||
os_ << errstr << " - ";
|
||||
os_ << err.str << std::endl;
|
||||
}
|
||||
|
||||
return os_;
|
||||
|
||||
+96
-13
@@ -16,29 +16,35 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "branch.hpp"
|
||||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "config_link_exdev.hpp"
|
||||
#include "config_log_metrics.hpp"
|
||||
#include "config_moveonenospc.hpp"
|
||||
#include "config_nfsopenhack.hpp"
|
||||
#include "config_readdir.hpp"
|
||||
#include "config_rename_exdev.hpp"
|
||||
#include "config_statfs.hpp"
|
||||
#include "config_statfsignore.hpp"
|
||||
#include "config_xattr.hpp"
|
||||
#include "enum.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "func_category.hpp"
|
||||
#include "funcs.hpp"
|
||||
#include "policy.hpp"
|
||||
#include "policy_cache.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "tofrom_wrapper.hpp"
|
||||
|
||||
#include <fuse.h>
|
||||
#include "fuse.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
@@ -47,20 +53,55 @@ typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
|
||||
extern const std::string CONTROLFILE;
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
struct Err
|
||||
{
|
||||
int err;
|
||||
std::string str;
|
||||
};
|
||||
|
||||
typedef std::vector<Err> ErrVec;
|
||||
|
||||
public:
|
||||
class Read
|
||||
{
|
||||
public:
|
||||
Read();
|
||||
|
||||
public:
|
||||
inline const Config* operator->() const;
|
||||
|
||||
private:
|
||||
const Config &_cfg;
|
||||
};
|
||||
|
||||
public:
|
||||
class Write
|
||||
{
|
||||
public:
|
||||
Write();
|
||||
|
||||
public:
|
||||
Config* operator->();
|
||||
|
||||
private:
|
||||
Config &_cfg;
|
||||
};
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
public:
|
||||
mutable PolicyCache open_cache;
|
||||
|
||||
public:
|
||||
const std::string controlfile;
|
||||
Config& operator=(const Config&);
|
||||
|
||||
public:
|
||||
ConfigBOOL async_read;
|
||||
ConfigBOOL auto_cache;
|
||||
ConfigUINT64 minfreespace;
|
||||
Branches branches;
|
||||
ConfigUINT64 cache_attr;
|
||||
ConfigUINT64 cache_entry;
|
||||
@@ -69,17 +110,19 @@ public:
|
||||
ConfigBOOL cache_readdir;
|
||||
ConfigUINT64 cache_statfs;
|
||||
ConfigBOOL cache_symlinks;
|
||||
FuncCategories category;
|
||||
Categories category;
|
||||
ConfigBOOL direct_io;
|
||||
ConfigBOOL dropcacheonclose;
|
||||
ConfigSTR fsname;
|
||||
FollowSymlinks follow_symlinks;
|
||||
Funcs func;
|
||||
ConfigUINT64 fuse_msg_size;
|
||||
ConfigBOOL ignorepponrename;
|
||||
InodeCalc inodecalc;
|
||||
ConfigBOOL kernel_cache;
|
||||
ConfigBOOL link_cow;
|
||||
ConfigUINT64 minfreespace;
|
||||
LinkEXDEV link_exdev;
|
||||
LogMetrics log_metrics;
|
||||
ConfigSTR mount;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
@@ -88,6 +131,7 @@ public:
|
||||
ConfigBOOL posix_acl;
|
||||
ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
RenameEXDEV rename_exdev;
|
||||
ConfigBOOL security_capability;
|
||||
SrcMounts srcmounts;
|
||||
StatFS statfs;
|
||||
@@ -112,11 +156,50 @@ public:
|
||||
int get(const std::string &key, std::string *val) const;
|
||||
int set_raw(const std::string &key, const std::string &val);
|
||||
int set(const std::string &key, const std::string &val);
|
||||
int set(const std::string &kv);
|
||||
|
||||
public:
|
||||
static const Config &ro(void);
|
||||
static Config &rw(void);
|
||||
int from_stream(std::istream &istrm, ErrVec *errs);
|
||||
int from_file(const std::string &filepath, ErrVec *errs);
|
||||
|
||||
private:
|
||||
Str2TFStrMap _map;
|
||||
|
||||
private:
|
||||
static Config _singleton;
|
||||
|
||||
public:
|
||||
friend class Read;
|
||||
friend class Write;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &s,const Config::ErrVec &ev);
|
||||
|
||||
inline
|
||||
Config::Read::Read()
|
||||
: _cfg(Config::_singleton)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
const
|
||||
Config*
|
||||
Config::Read::operator->() const
|
||||
{
|
||||
return &_cfg;
|
||||
}
|
||||
|
||||
inline
|
||||
Config::Write::Write()
|
||||
: _cfg(Config::_singleton)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline
|
||||
Config*
|
||||
Config::Write::operator->()
|
||||
{
|
||||
return &_cfg;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class CacheFilesEnum
|
||||
{
|
||||
LIBFUSE,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
FollowSymlinks::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case FollowSymlinks::ENUM::NEVER:
|
||||
return "never";
|
||||
case FollowSymlinks::ENUM::DIRECTORY:
|
||||
return "directory";
|
||||
case FollowSymlinks::ENUM::REGULAR:
|
||||
return "regular";
|
||||
case FollowSymlinks::ENUM::ALL:
|
||||
return "all";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
FollowSymlinks::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "never")
|
||||
_data = FollowSymlinks::ENUM::NEVER;
|
||||
ef(s_ == "directory")
|
||||
_data = FollowSymlinks::ENUM::DIRECTORY;
|
||||
ef(s_ == "regular")
|
||||
_data = FollowSymlinks::ENUM::REGULAR;
|
||||
ef(s_ == "all")
|
||||
_data = FollowSymlinks::ENUM::ALL;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
enum class FollowSymlinksEnum
|
||||
{
|
||||
NEVER,
|
||||
DIRECTORY,
|
||||
REGULAR,
|
||||
ALL
|
||||
};
|
||||
typedef Enum<FollowSymlinksEnum> FollowSymlinks;
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "config_inodecalc.hpp"
|
||||
#include "fs_inode.hpp"
|
||||
|
||||
|
||||
InodeCalc::InodeCalc(const std::string &s_)
|
||||
{
|
||||
fs::inode::set_algo(s_);
|
||||
|
||||
@@ -20,12 +20,13 @@
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
|
||||
class InodeCalc : public ToFromString
|
||||
{
|
||||
public:
|
||||
InodeCalc(const std::string &);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const;
|
||||
int from_string(const std::string &);
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string &) final;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config_link_exdev.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
LinkEXDEV::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case LinkEXDEV::ENUM::PASSTHROUGH:
|
||||
return "passthrough";
|
||||
case LinkEXDEV::ENUM::REL_SYMLINK:
|
||||
return "rel-symlink";
|
||||
case LinkEXDEV::ENUM::ABS_BASE_SYMLINK:
|
||||
return "abs-base-symlink";
|
||||
case LinkEXDEV::ENUM::ABS_POOL_SYMLINK:
|
||||
return "abs-pool-symlink";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
LinkEXDEV::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "passthrough")
|
||||
_data = LinkEXDEV::ENUM::PASSTHROUGH;
|
||||
ef(s_ == "rel-symlink")
|
||||
_data = LinkEXDEV::ENUM::REL_SYMLINK;
|
||||
ef(s_ == "abs-base-symlink")
|
||||
_data = LinkEXDEV::ENUM::ABS_BASE_SYMLINK;
|
||||
ef(s_ == "abs-pool-symlink")
|
||||
_data = LinkEXDEV::ENUM::ABS_POOL_SYMLINK;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
enum class LinkEXDEVEnum
|
||||
{
|
||||
PASSTHROUGH,
|
||||
REL_SYMLINK,
|
||||
ABS_BASE_SYMLINK,
|
||||
ABS_POOL_SYMLINK
|
||||
};
|
||||
typedef Enum<LinkEXDEVEnum> LinkEXDEV;
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config_log_metrics.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "to_string.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
LogMetrics::LogMetrics(const bool val_)
|
||||
{
|
||||
fuse_log_metrics_set(val_);
|
||||
}
|
||||
|
||||
std::string
|
||||
LogMetrics::to_string(void) const
|
||||
{
|
||||
bool val;
|
||||
|
||||
val = fuse_log_metrics_get();
|
||||
|
||||
return str::to(val);
|
||||
}
|
||||
|
||||
int
|
||||
LogMetrics::from_string(const std::string &s_)
|
||||
{
|
||||
int rv;
|
||||
bool val;
|
||||
|
||||
rv = str::from(s_,&val);
|
||||
if(rv < 0)
|
||||
return rv;
|
||||
|
||||
fuse_log_metrics_set(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
class LogMetrics : public ToFromString
|
||||
{
|
||||
public:
|
||||
LogMetrics(const bool);
|
||||
|
||||
public:
|
||||
std::string to_string(void) const final;
|
||||
int from_string(const std::string &) final;
|
||||
};
|
||||
@@ -21,12 +21,13 @@
|
||||
#include "errno.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
|
||||
int
|
||||
MoveOnENOSPC::from_string(const std::string &s_)
|
||||
{
|
||||
int rv;
|
||||
std::string s;
|
||||
const Policy *tmp;
|
||||
Policy::CreateImpl *tmp;
|
||||
|
||||
rv = str::from(s_,&enabled);
|
||||
if((rv == 0) && (enabled == true))
|
||||
@@ -36,8 +37,8 @@ MoveOnENOSPC::from_string(const std::string &s_)
|
||||
else
|
||||
return 0;
|
||||
|
||||
tmp = &Policy::find(s);
|
||||
if(tmp == Policy::invalid)
|
||||
tmp = Policies::Create::find(s);
|
||||
if(tmp == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
policy = tmp;
|
||||
@@ -50,6 +51,6 @@ std::string
|
||||
MoveOnENOSPC::to_string(void) const
|
||||
{
|
||||
if(enabled)
|
||||
return policy->to_string();
|
||||
return policy.name();
|
||||
return "false";
|
||||
}
|
||||
|
||||
@@ -19,26 +19,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "policy.hpp"
|
||||
#include "policies.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class MoveOnENOSPC : public ToFromString
|
||||
{
|
||||
public:
|
||||
MoveOnENOSPC(const bool enabled_)
|
||||
: enabled(enabled_)
|
||||
: enabled(enabled_),
|
||||
policy(&Policies::Create::mfs)
|
||||
{
|
||||
policy = (enabled ?
|
||||
&Policy::mfs :
|
||||
&Policy::invalid);
|
||||
}
|
||||
|
||||
public:
|
||||
int from_string(const std::string &s);
|
||||
std::string to_string() const;
|
||||
int from_string(const std::string &s) final;
|
||||
std::string to_string() const final;
|
||||
|
||||
public:
|
||||
bool enabled;
|
||||
const Policy *policy;
|
||||
Policy::Create policy;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
int
|
||||
NFSOpenHack::from_string(const std::string &s_)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class NFSOpenHackEnum
|
||||
{
|
||||
OFF,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
int
|
||||
ReadDir::from_string(const std::string &s_)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class ReadDirEnum
|
||||
{
|
||||
POSIX,
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config_rename_exdev.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
RenameEXDEV::to_string(void) const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case RenameEXDEV::ENUM::PASSTHROUGH:
|
||||
return "passthrough";
|
||||
case RenameEXDEV::ENUM::REL_SYMLINK:
|
||||
return "rel-symlink";
|
||||
case RenameEXDEV::ENUM::ABS_SYMLINK:
|
||||
return "abs-symlink";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
template<>
|
||||
int
|
||||
RenameEXDEV::from_string(const std::string &s_)
|
||||
{
|
||||
if(s_ == "passthrough")
|
||||
_data = RenameEXDEV::ENUM::PASSTHROUGH;
|
||||
ef(s_ == "rel-symlink")
|
||||
_data = RenameEXDEV::ENUM::REL_SYMLINK;
|
||||
ef(s_ == "abs-symlink")
|
||||
_data = RenameEXDEV::ENUM::ABS_SYMLINK;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
enum class RenameEXDEVEnum
|
||||
{
|
||||
PASSTHROUGH,
|
||||
REL_SYMLINK,
|
||||
ABS_SYMLINK
|
||||
};
|
||||
typedef Enum<RenameEXDEVEnum> RenameEXDEV;
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
|
||||
template<>
|
||||
std::string
|
||||
StatFS::to_string() const
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class StatFSEnum
|
||||
{
|
||||
BASE,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user