libfuse升级

Signed-off-by: psycho <wangmingxuan6@h-partners.com>
This commit is contained in:
psycho
2024-01-08 09:34:29 +08:00
parent c96dbc71fa
commit f89452c304
74 changed files with 5194 additions and 1176 deletions
+11
View File
@@ -0,0 +1,11 @@
[codespell]
skip = .git,*.pdf,*.svg,AUTHORS
# The following strings shouldn't actually be accepted, but they're wrongly
# identified as words and there is currently no way to exclude them on
# a by-line basis (https://github.com/codespell-project/codespell/pull/2400).
# Therefore, pretend that they are correctly spelled words:
# - alse: used in regex
# - siz: wanted short
# - fiter: variable
ignore-words-list = alse,siz,fiter
+36
View File
@@ -16,6 +16,7 @@ Contributors
CUSE has been written by Tejun Heo <teheo@suse.de>. Furthermore, the
following people have contributed patches (autogenerated list):
1c7718e7 <shirokovMartin@gmail.com>
a1346054 <36859588+a1346054@users.noreply.github.com>
admorgan <admorgan@morgancomputers.net>
@@ -24,12 +25,14 @@ AKowshik <AKowshik@users.noreply.github.com>
Alan Somers <asomers@gmail.com>
Albert Chen <58009229+hselin-kalista-io@users.noreply.github.com>
Albert Chen <hselin.chen@gmail.com>
Aleksandr Mikhailov <AM5800@users.noreply.github.com>
Alexander <aleksandr.rvachev@eltex-co.ru>
alex <devkral@web.de>
Alex Richman <alex@richman.io>
Amir Goldstein <amir73il@gmail.com>
amosonn <amosonn@gmail.com>
Anatol Pomozov <anatol.pomozov@gmail.com>
André Schröder <andre.schroeder@andresco.de>
Andrew Gaul <andrew@gaul.org>
Andrew Gaul <gaul@google.com>
Angelo G. Del Regno <kholk11@gmail.com>
@@ -42,6 +45,8 @@ AsumFace <asumface@gmail.com>
Banglang <banglang.huang@foxmail.com>
Baptiste Daroussin <bapt@FreeBSD.org>
Benjamin Barenblat <bbaren@google.com>
Bernd Schubert <bernd.schubert@fastmail.fm>
Bernd Schubert <bschubert@ddn.com>
Bill Zissimooulos <billziss@navimatics.com>
Bill Zissimopoulos <billziss@navimatics.com>
bobrofon <ifbossfor@ya.ru>
@@ -52,6 +57,7 @@ Chad Austin <chad@chadaustin.me>
Changli Gao <xiaosuo@gmail.com>
Christian Menges <christian.menges@tum.de>
Christopher Harrison <ch12@sanger.ac.uk>
Ciaran <ciaran@hypokrites.me>
Consus <consus@gmx.com>
Craig Chi <craig08@users.noreply.github.com>
Csaba Henk <csaba.henk@creo.hu>
@@ -60,8 +66,11 @@ cvs2git <>
Dalvik Khertel <khertel@outlook.com>
Daniel Fullmer <danielrf12@gmail.com>
Daniel Thau <danthau@bedrocklinux.org>
David Galeano <davidgaleano@gmail.com>
David McNab <david@rebirthing.co.nz>
David Sheets <sheets@alum.mit.edu>
Dharmendra singh <dsingh@ddn.com>
Dharmendra Singh <dsingh@ddn.com>
divinity76 <divinity76@gmail.com>
DrDaveD <2129743+DrDaveD@users.noreply.github.com>
Dr. David Alan Gilbert <dgilbert@redhat.com>
@@ -78,16 +87,21 @@ Fedor Korotkov <fedor.korotkov@gmail.com>
Feng Shuo <steve.shuo.feng@gmail.com>
ferivoz <72023087+ferivoz@users.noreply.github.com>
Feverfew <Feverfew@users.noreply.github.com>
Fina Wilke <code@felinira.net>
Florian Weimer <fw@deneb.enyo.de>
Forty-Bot <Forty-Bot@users.noreply.github.com>
Frank Dinoff <fdinoff@google.com>
Giulio Benetti <giulio.benetti@benettiengineering.com>
Giuseppe Scrivano <giuseppe@scrivano.org>
Goswin von Brederlow <goswin-v-b@web.de>
guraga <rulumasi@dodsi.com>
HazelFZ <xfzfygz@gmail.com>
Heiko Becker <heirecka@exherbo.org>
Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
HereThereBeDragons <HereThereBeDragons@users.noreply.github.com>
Hookey <Hookey@users.noreply.github.com>
human <human@neet.fi>
ikbenlike <ikbenlike@users.noreply.github.com>
Ikey Doherty <michael.i.doherty@intel.com>
itsdeepak <deepak.sn@samsung.com>
Jan Blumschein <jan@jan-blumschein.de>
@@ -107,15 +121,19 @@ Junichi Uekawa <dancer@debian.org>
Junichi Uekawa <dancerj@gmail.com>
Junichi Uekawa <dancer@netfort.gr.jp>
Kangjing "Chaser" Huang <huangkangjing@gmail.com>
Ken Schalk <kcs-github@xorian.net>
Kevin Vigor <kvigor@fb.com>
Kirill Smelkov <kirr@nexedi.com>
Kyle Lippincott <spectral@google.com>
Laszlo Boszormenyi (GCS) <gcs@debian.org>
Laszlo Papp <ext-laszlo.papp@nokia.com>
Laurent Bigonville <bigon@users.noreply.github.com>
Lilo Huang <lilohuang@users.noreply.github.com>
Liu Bo <liub.liubo@gmail.com>
Li-Wen Hsu <lwhsu.github@lwhsu.org>
lixiaokeng <63774002+lixiaokeng@users.noreply.github.com>
lixiaokeng <lixiaokeng@huawei.com>
Luis Henriques <luis-henrix@users.noreply.github.com>
Madan Valluri <mvalluri@cumulus-systems.com>
Manuel Jacob <me@manueljacob.de>
Marcin Sulikowski <marcin.sulikowski@editshare.com>
@@ -123,8 +141,11 @@ Mark Glines <mark@glines.org>
Martin Blanchard <tchaik@gmx.com>
Martin Pärtel <martin.partel@gmail.com>
Mateusz Urbańczyk <murbanczyk@qed.ai>
Matthias Goergens <matthias.goergens@gmail.com>
Matthias Görgens <matthias.goergens@gmail.com>
Mattias Nissler <mnissler@chromium.org>
maxice8 <30738253+maxice8@users.noreply.github.com>
Maximilian Heinzler <m.heinzler@heinzler.de>
Max Krasnyansky <maxk@kernel.org>
Michael Forney <mforney@mforney.org>
Michael Grigoriev <mag@luminal.org>
@@ -138,13 +159,19 @@ mkmm@gmx-topmail.de <mkmm@gmx-topmail.de>
Natanael Copa <ncopa@alpinelinux.org>
Niels de Vos <ndevos@redhat.com>
Nikolaus Rath <Nikolaus@rath.org>
Nozomi Miyamori <99280467+nm004@users.noreply.github.com>
Oded Arbel <oded@geek.co.il>
Olivier Blin <olivier.blin@softathome.com>
pablomh <pablomh@gmail.com>
Pedro Kaj Kjellerup Nacht <pnacht@google.com>
Pedro Nacht <pnacht@google.com>
Peri <peri@srdi.org>
Peter Lemenkov <lemenkov@gmail.com>
philmd <philmd@users.noreply.github.com>
Pierre Labastie <pierre.labastie@neuf.fr>
Przemyslaw Pawelczyk <przemoc@gmail.com>
Przemysław Pawełczyk <przemoc@gmail.com>
psykose <alice@ayaya.dev>
Ratna_Bolla@dell.com <Ratna_Bolla@dell.com>
Rethan <359062468@qq.con>
Reuben Hawkins <reubenhwk@gmail.com>
@@ -158,10 +185,12 @@ Roman Bogorodskiy <bogorodskiy@gmail.com>
Rosen Penev <rosenp@gmail.com>
Rostislav <rostislav@users.noreply.github.com>
Rostislav Skudnov <rostislav@tuxera.com>
Rudi Heitbaum <rudi@heitbaum.com>
Sam Huffman <40582525+samh-sifive@users.noreply.github.com>
Sam James <sam@gentoo.org>
Sam Stuewe <halosghost@archlinux.info>
Sangwoo Moon <swmoon00@gmail.com>
Sarath Lakshman <sarath@couchbase.com>
Sargun Dhillon <sargun@sargun.me>
scosu <mpargmann@allfex.org>
Scott Worley <scottworley@scottworley.com>
@@ -176,6 +205,7 @@ Tej Chajed <tchajed@mit.edu>
tenzap <46226844+tenzap@users.noreply.github.com>
therealneworld@gmail.com <therealneworld@gmail.com>
Tobias Nießen <tniessen@users.noreply.github.com>
Tofik Sonono <tofik.sonono@intel.com>
Tomasz Kulasek <34129113+tkulasek@users.noreply.github.com>
Tom Callaway <spot@fedoraproject.org>
Tom Callaway <spotrh@gmail.com>
@@ -183,9 +213,15 @@ Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
userwithuid <userwithuid@gmail.com>
Valentin Plugaru <valentin.plugaru@uni.lu>
Vivek Goyal <vgoyal@redhat.com>
Waldir Pimenta <waldyrious@gmail.com>
wdlkmpx <wdlkmpx@gmail.com>
William Woodruff <william@yossarian.net>
Winfried Koehler <w_scan@gmx-topmail.de>
winndows <winndows@163.com>
Xiubo Li <xiubli@redhat.com>
Yaroslav Halchenko <debian@onerussian.com>
y <tofik.sonono@intel.com>
Yuri Per <yuri@acronis.com>
Zhansong Gao <zhsgao@hotmail.com>
Zhiqiang Liu <liuzhiqiang26@huawei.com>
zsugabubus <zsugabubus@users.noreply.github.com>
+58 -57
View File
@@ -14,71 +14,72 @@
import("//build/ohos.gni")
libfuse_source = [
"//third_party/libfuse/lib/fuse.c",
"//third_party/libfuse/lib/fuse_loop.c",
"//third_party/libfuse/lib/fuse_loop_mt.c",
"//third_party/libfuse/lib/fuse_lowlevel.c",
"//third_party/libfuse/lib/fuse_opt.c",
"//third_party/libfuse/lib/fuse_signals.c",
"//third_party/libfuse/lib/buffer.c",
"//third_party/libfuse/lib/cuse_lowlevel.c",
"//third_party/libfuse/lib/helper.c",
"//third_party/libfuse/lib/modules/subdir.c",
"//third_party/libfuse/lib/mount_util.c",
"//third_party/libfuse/lib/fuse_log.c",
"//third_party/libfuse/lib/mount.c",
"//third_party/libfuse/lib/modules/iconv.c",
"//third_party/libfuse/lib/fuse.c",
"//third_party/libfuse/lib/fuse_loop.c",
"//third_party/libfuse/lib/fuse_loop_mt.c",
"//third_party/libfuse/lib/fuse_lowlevel.c",
"//third_party/libfuse/lib/fuse_opt.c",
"//third_party/libfuse/lib/fuse_signals.c",
"//third_party/libfuse/lib/buffer.c",
"//third_party/libfuse/lib/compat.c",
"//third_party/libfuse/lib/cuse_lowlevel.c",
"//third_party/libfuse/lib/helper.c",
"//third_party/libfuse/lib/modules/subdir.c",
"//third_party/libfuse/lib/mount_util.c",
"//third_party/libfuse/lib/fuse_log.c",
"//third_party/libfuse/lib/mount.c",
"//third_party/libfuse/lib/modules/iconv.c",
]
import("//build/ohos.gni")
config("libfuse_config") {
visibility = [ ":*" ]
visibility = [ ":*" ]
include_dirs = [
"//third_party/libfuse/include",
"//third_party/libfuse/lib",
"//third_party/libfuse",
]
include_dirs = [
"//third_party/libfuse/include",
"//third_party/libfuse/lib",
"//third_party/libfuse",
]
cflags = [
"-fdiagnostics-color=always",
"-pipe",
"-D_FILE_OFFSET_BITS=64",
"-Winvalid-pch",
"-Wextra",
"-O2",
"-g",
"-D_REENTRANT",
"-Wno-sign-compare",
"-Wmissing-declarations",
"-Wwrite-strings",
"-Wno-unused-result",
"-DFUSE_USE_VERSION=35",
"-DFUSERMOUNT_DIR=\"/usr/local/bin\"",
"-U_GNU_SOURCE",
"-pthread"
]
cflags = [
"-fdiagnostics-color=always",
"-pipe",
"-D_FILE_OFFSET_BITS=64",
"-Winvalid-pch",
"-Wextra",
"-O2",
"-g",
"-D_REENTRANT",
"-Wno-sign-compare",
"-Wmissing-declarations",
"-Wwrite-strings",
"-Wno-unused-result",
"-DFUSE_USE_VERSION=312",
"-DFUSERMOUNT_DIR=\"/usr/local/bin\"",
"-U_GNU_SOURCE",
"-pthread",
]
}
ohos_shared_library("libfuse") {
configs = [ "//third_party/libfuse:libfuse_config" ]
sources = libfuse_source
license_file = "./LGPL2.txt"
ldflags = [
"-ldl",
"-Wl,--version-script",
rebase_path("//third_party/libfuse/lib/fuse_versionscript", root_build_dir),
"-Wl,-soname,libfuse3.so.3",
"-Wl,--no-undefined",
"-Wl,--as-needed",
"-shared",
"-fPIC",
]
external_deps = [ "c_utils:utils" ]
subsystem_name = "thirdparty"
part_name = "libfuse"
output_name = "libfuse"
install_enable = true
install_images = [ "system" ]
configs = [ "//third_party/libfuse:libfuse_config" ]
sources = libfuse_source
license_file = "./LGPL2.txt"
ldflags = [
"-ldl",
"-Wl,--version-script",
rebase_path("//third_party/libfuse/lib/fuse_versionscript", root_build_dir),
"-Wl,-soname,libfuse3.so.3",
"-Wl,--no-undefined",
"-Wl,--as-needed",
"-shared",
"-fPIC",
]
external_deps = [ "c_utils:utils" ]
subsystem_name = "thirdparty"
part_name = "libfuse"
output_name = "libfuse"
install_enable = true
install_images = [ "system" ]
}
+104 -4
View File
@@ -1,8 +1,108 @@
libfuse 3.16.1 (2023-08-08)
===========================
* Readdir kernel cache can be enabled from high-level API.
libfuse 3.15.1 (2023-07-05)
===========================
Future libfuse releases will be signed with `signify`_ rather than PGP (rationale_). This
release is the last to be signed with PGP and contains the signify public key for current
(3.15.X) and upcoming (3.16.X) minor release cycle.
.. _signify: https://www.openbsd.org/papers/bsdcan-signify.html
.. _rationale: https://latacora.micro.blog/2019/07/16/the-pgp-problem.html
libfuse 3.15.0 (2023-06-09)
===========================
* Improved support for some less common systems (32 bit, alternative libcs)
* Unsupported mount options are no longer silently accepted.
* auto_unmount is now compatible with allow_other.
libfuse 3.14.1 (2023-03-26)
===========================
* The extended attribute name passed to the setxattr() handler is no longer
truncated at the beginning (bug introduced in 3.13.0).
* As a result of the above, the additional setattr() flags introduced in 3.14 are no
longer available for now. They will hopefully be reintroduced in the next release.
* Further improvements of configuration header handling.
libfuse 3.14.0 (2023-02-17)
===========================
* Properly fix the header installation issue. The fix in 3.13.1 resulted
in conflicts with other packages.
* Introduce additional setattr() flags (FORCE, KILL_SUID, KILL_SGID, FILE, KILL_PRIV,
OPEN, TIMES_SET)
libfuse 3.13.1 (2023-02-03)
===========================
* Fixed an issue that resulted in errors when attempting to compile against
installed libfuse headers (because libc symbol versioning support was not
detected correctly in this case).
libfuse 3.13.0 (2023-01-13)
===========================
* There is a new low-level API function `fuse_session_custom_io` that allows to implement
a daemon with a custom io. This can be used to create a daemon that can process incoming
FUSE requests to other destinations than `/dev/fuse`.
* A segfault when loading custom FUSE modules has been fixed.
* There is a new `fuse_notify_expire_entry` function.
* A deadlock when resolving paths in the high-level API has been fixed.
* libfuse can now be build explicitly for C libraries without symbol versioning support.
libfuse 3.12.0 (2022-09-08)
===========================
* There is a new build parameter to specify where the SysV init script should be
installed.
* The *max_idle_threads* parameter has been deprecated in favor of the new max_threads*
parameter (which avoids the excessive overhead of creating and destructing threads).
Using max_threads == 1 and calling fuse_session_loop_mt() will run single threaded
similar to fuse_session_loop().
The following changes apply when using the most recent API (-DFUSE_USE_VERSION=312,
see `example/passthrough_hp.cc` for an example for how to usse the new API):
* `struct fuse_loop_config` is now private and has to be constructed using
*fuse_loop_cfg_create()* and destroyed with *fuse_loop_cfg_destroy()*. Parameters can be
changed using `fuse_loop_cfg_set_*()` functions.
* *fuse_session_loop_mt()* now accepts `struct fuse_loop_config *` as NULL pointer.
* *fuse_parse_cmdline()* now accepts a *max_threads* option.
libfuse 3.11.0 (2022-05-02)
===========================
* Add support for flag FOPEN_NOFLUSH for avoiding flush on close.
* Fixed returning an error condition to ioctl(2)
libfuse 3.10.5 (2021-09-06)
===========================
* Various improvements to make unit tests more robust.
libfuse 3.10.4 (2021-06-09)
===========================
@@ -422,7 +522,7 @@ libfuse 3.0.0 (2016-12-08)
* The ``-o nopath`` option has been dropped - it never actually did
anything (since it is unconditionally overwritten with the value of
the `nopath` flag in `struct fuse_operations).
the `nopath` flag in `struct fuse_operations`).
* The ``-o large_read`` mount option has been dropped. Hopefully no
one uses a Linux 2.4 kernel anymore.
@@ -446,7 +546,7 @@ libfuse 3.0.0 (2016-12-08)
* The `fuse_session_new` function no longer accepts the ``-o
clone_fd`` option. Instead, this has become a parameter of the
`fuse_session_loop_mt` and ``fuse_loop_mt` functions.
`fuse_session_loop_mt` and `fuse_loop_mt` functions.
* For low-level file systems that implement the `write_buf` handler,
the `splice_read` option is now enabled by default. As usual, this
@@ -636,7 +736,7 @@ libfuse 3.0.0 (2016-12-08)
* The *fuse_off_t* and *fuse_ino_t* changed from *unsigned long* to
*uint64_t*, i.e. they are now 64 bits also on 32-bit systems.
* The type of the *generation* member of `struct fuse_entry_param*
* The type of the *generation* member of `struct fuse_entry_param*`
changed from *unsigned* to *uint64_t*.
* The (low-level) `setattr` handler gained a *FUSE_SET_ATTR_CTIME* bit
+2 -2
View File
@@ -3,9 +3,9 @@
"Name" : "libfuse",
"License" : "LGPL V2.1",
"License File" : "LGPL2.txt",
"Version Number" : "3.10.5",
"Version Number" : "3.16.1",
"Owner" : "maojingjing1@huawei.com",
"Upstream URL" : "https://github.com/libfuse/libfuse/releases/tag/fuse-3.10.5",
"Upstream URL" : "https://github.com/libfuse/libfuse/releases/tag/fuse-3.16.1",
"Description" : "libfuse provides the reference implementation for communicating with the FUSE kernel module."
}
]
+16 -6
View File
@@ -49,14 +49,24 @@ Supported Platforms
Installation
------------
You can download libfuse from
https://github.com/libfuse/libfuse/releases. To build and install, you
must use [Meson](http://mesonbuild.com/) and
[Ninja](https://ninja-build.org). After extracting the libfuse
tarball, create a (temporary) build directory and run Meson:
You can download libfuse from https://github.com/libfuse/libfuse/releases. To build and
install, you must use [Meson](http://mesonbuild.com/) and
[Ninja](https://ninja-build.org). After downloading the tarball and `.sig` file, verify
it using [signify](https://www.openbsd.org/papers/bsdcan-signify.html):
signify -V -m fuse-X.Y.Z.tar.gz -p fuse-X.Y.pub
The `fuse-X.Y.pub` file contains the signing key and needs to be obtained from a
trustworthy source. Each libfuse release contains the signing key for the release after it
in the `signify` directory, so you only need to manually acquire this file once when you
install libfuse for the first time.
After you have validated the tarball, extract it, create a (temporary) build directory and
run Meson:
$ tar xzf fuse-X.Y.Z.tar.gz; cd fuse-X.Y.Z
$ mkdir build; cd build
$ meson ..
$ meson setup ..
Normally, the default build options will work fine. If you
nevertheless want to adjust them, you can do so with the
+1
View File
@@ -35,6 +35,7 @@ libfuse_source = [
"//third_party/libfuse/lib/fuse_opt.c",
"//third_party/libfuse/lib/fuse_signals.c",
"//third_party/libfuse/lib/buffer.c",
"//third_party/libfuse/lib/compat.c",
"//third_party/libfuse/lib/cuse_lowlevel.c",
"//third_party/libfuse/lib/helper.c",
"//third_party/libfuse/lib/modules/subdir.c",
+20
View File
@@ -0,0 +1,20 @@
# Security Policy
If you have discovered a security vulnerability in this project, please report it
privately. **Do not disclose it as a public issue.** This gives me time to work with you
to fix the issue before public exposure, reducing the chance that the exploit will be
used before a patch is released.
Please submit information on the vulnerability as a
[private report](https://github.com/libfuse/libfuse/security/advisories/new).
Please provide the following information in your report:
- A description of the vulnerability and its impact
- How to reproduce the issue
This project is maintained by a single volunteer on a reasonable-effort basis. As such,
I ask that you give me 90 days to work on a fix before public exposure.
Note we are aware of a long-standing security issue when using `allow_others` (see
[#15](https://github.com/libfuse/libfuse/issues/15)).
+4 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@ohos/libfuse",
"description": "libfuse provides the reference implementation for communicating with the FUSE kernel module.",
"version": "3.10.5",
"version": "3.16.1",
"license": "LGPL V2.1",
"publishAs": "code-segment",
"segment": {
@@ -18,7 +18,9 @@
"subsystem": "thirdparty",
"syscap": [],
"features": [],
"adapted_system_type": [],
"adapted_system_type": [
"standard"
],
"rom": "",
"ram": "",
"deps": {
+58
View File
@@ -0,0 +1,58 @@
Release Process
===============
* `set TAG fuse-A.B.C`
* Update version in
* `ChangeLog.rst`
* `meson.build`
* `include/fuse_common.h` (`#define FUSE_{MINOR/MAJOR}_VERSION`)
* When creating new minor release:
* Create signing key for the next release: `P=fuse-<A.B+1> signify-openbsd -G -n -p signify/$P.pub -s
signify/$P.sec`
* Expire old release signing keys (keep one around just in case)
* Update authors: `git log --all --pretty="format:%an <%aE>" | sort -u >> AUTHORS`
* `git commit --all -m "Released $TAG"`
* `git tag -s $TAG`
* Build tarball, `./make_release_tarball.sh`
* Test build:
* `cd fuse-x.y.z`
* `md build && (cd build && meson .. && ninja)`
* `sudo sudo chown root:root build/util/fusermount3`
* `sudo chmod 4755 build/util/fusermount3`
* `(cd build; python3 -m pytest test/)`
* Upload API docs:
* `rm -r ../libfuse.github.io/doxygen && cp -a doc/html ../libfuse.github.io/doxygen`
* `(cd ../libfuse.github.io; git add doxygen/; git commit --all -m "Re-generated doxygen documentation"; git push)`
* `git push && git push --tags`, create release on Github
* Write announcement to fuse-devel
Announcement email template
```
To: fuse-devel@lists.sourceforge.net
Subject: [ANNOUNCE] libfuse XXXX has been released
Dear all,
I am pleased to announce the release of libfuse XXX.
The source code is available for download at https://github.com/libfuse/libfuse/releases.
Please report any issues on this mailing list or the GitHub issue
tracker at https://github.com/libfuse/libfuse/issues.
From ChangeLog.rst:
[INSERT NEW ENTRIES]
The following people have contributed code to this release:
[INSERT CONTRIBUTORS]
(a full list of credits containing all known contributors is included in
the `AUTHORS` file).
Best,
-Nikolaus
```
+2096 -636
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -146,6 +146,21 @@ responsible for releasing the mountpoint, which means that the
mountpoint becomes inaccessible if the filesystem terminates
without first unmounting.
This option is dangerous and should only be used after careful consideration of the
risks.
Automatically unmounting the filesystem means that if the filesystem crashes the
mountpoint may suddenly appear empty, which may have unintended consequences. For example,
a running backup and mirroring program may conclude that all the data in the filesystem
has been deleted and proceed to propagate this deletion to the backup / remote system. If
the mountpoint instead becomes inaccessible (the default), most programs will behave
correctly (report an error).
This feature may also accidentally unmount the wrong filesystem due to race
conditions. For example, if another filesystem was mounted underneath the same mountpoint,
or if a new filesystem is mounted after the FUSE process has crashed, it may accidentally
get unmounted.
At the moment, this option implies that the filesystem will also be
mounted with \fBnodev\fP and \fBnosuid\fP (even when mounted by
root). This restriction may be lifted in the future.
+56 -5
View File
@@ -153,12 +153,63 @@ static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
}
static void hello_ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
size_t size)
{
(void)size;
assert(ino == 2);
if (strcmp(name, "hello_ll_getxattr_name") == 0)
{
const char *buf = "hello_ll_getxattr_value";
fuse_reply_buf(req, buf, strlen(buf));
}
else
{
fuse_reply_err(req, ENOTSUP);
}
}
static void hello_ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags)
{
(void)flags;
(void)size;
assert(ino == 2);
const char* exp_val = "hello_ll_setxattr_value";
if (strcmp(name, "hello_ll_setxattr_name") == 0 &&
strlen(exp_val) == size &&
strncmp(value, exp_val, size) == 0)
{
fuse_reply_err(req, 0);
}
else
{
fuse_reply_err(req, ENOTSUP);
}
}
static void hello_ll_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
assert(ino == 2);
if (strcmp(name, "hello_ll_removexattr_name") == 0)
{
fuse_reply_err(req, 0);
}
else
{
fuse_reply_err(req, ENOTSUP);
}
}
static const struct fuse_lowlevel_ops hello_ll_oper = {
.lookup = hello_ll_lookup,
.getattr = hello_ll_getattr,
.readdir = hello_ll_readdir,
.open = hello_ll_open,
.read = hello_ll_read,
.lookup = hello_ll_lookup,
.getattr = hello_ll_getattr,
.readdir = hello_ll_readdir,
.open = hello_ll_open,
.read = hello_ll_read,
.setxattr = hello_ll_setxattr,
.getxattr = hello_ll_getxattr,
.removexattr = hello_ll_removexattr,
};
int main(int argc, char *argv[])
+358
View File
@@ -0,0 +1,358 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (C) 2022 Tofik Sonono <tofik.sonono@intel.com>
This program can be distributed under the terms of the GNU GPLv2.
See the file COPYING.
*/
/** @file
*
* minimal example filesystem using low-level API and a custom io. This custom
* io is implemented using UNIX domain sockets (of type SOCK_STREAM)
*
* Compile with:
*
* gcc -Wall hello_ll_uds.c `pkg-config fuse3 --cflags --libs` -o hello_ll_uds
*
* ## Source code ##
* \include hello_ll.c
*/
#define FUSE_USE_VERSION 34
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <fuse_lowlevel.h>
#include <fuse_kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
static const char *hello_str = "Hello World!\n";
static const char *hello_name = "hello";
static int hello_stat(fuse_ino_t ino, struct stat *stbuf)
{
stbuf->st_ino = ino;
switch (ino) {
case 1:
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
break;
case 2:
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(hello_str);
break;
default:
return -1;
}
return 0;
}
static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
struct stat stbuf;
(void) fi;
memset(&stbuf, 0, sizeof(stbuf));
if (hello_stat(ino, &stbuf) == -1)
fuse_reply_err(req, ENOENT);
else
fuse_reply_attr(req, &stbuf, 1.0);
}
static void hello_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
struct fuse_entry_param e;
if (parent != 1 || strcmp(name, hello_name) != 0)
fuse_reply_err(req, ENOENT);
else {
memset(&e, 0, sizeof(e));
e.ino = 2;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
hello_stat(e.ino, &e.attr);
fuse_reply_entry(req, &e);
}
}
struct dirbuf {
char *p;
size_t size;
};
static void dirbuf_add(fuse_req_t req, struct dirbuf *b, const char *name,
fuse_ino_t ino)
{
struct stat stbuf;
size_t oldsize = b->size;
b->size += fuse_add_direntry(req, NULL, 0, name, NULL, 0);
b->p = (char *) realloc(b->p, b->size);
memset(&stbuf, 0, sizeof(stbuf));
stbuf.st_ino = ino;
fuse_add_direntry(req, b->p + oldsize, b->size - oldsize, name, &stbuf,
b->size);
}
#define min(x, y) ((x) < (y) ? (x) : (y))
static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
off_t off, size_t maxsize)
{
if (off < bufsize)
return fuse_reply_buf(req, buf + off,
min(bufsize - off, maxsize));
else
return fuse_reply_buf(req, NULL, 0);
}
static void hello_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void) fi;
if (ino != 1)
fuse_reply_err(req, ENOTDIR);
else {
struct dirbuf b;
memset(&b, 0, sizeof(b));
dirbuf_add(req, &b, ".", 1);
dirbuf_add(req, &b, "..", 1);
dirbuf_add(req, &b, hello_name, 2);
reply_buf_limited(req, b.p, b.size, off, size);
free(b.p);
}
}
static void hello_ll_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
if (ino != 2)
fuse_reply_err(req, EISDIR);
else if ((fi->flags & O_ACCMODE) != O_RDONLY)
fuse_reply_err(req, EACCES);
else
fuse_reply_open(req, fi);
}
static void hello_ll_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, struct fuse_file_info *fi)
{
(void) fi;
assert(ino == 2);
reply_buf_limited(req, hello_str, strlen(hello_str), off, size);
}
static const struct fuse_lowlevel_ops hello_ll_oper = {
.lookup = hello_ll_lookup,
.getattr = hello_ll_getattr,
.readdir = hello_ll_readdir,
.open = hello_ll_open,
.read = hello_ll_read,
};
static int create_socket(const char *socket_path) {
struct sockaddr_un addr;
if (strnlen(socket_path, sizeof(addr.sun_path)) >=
sizeof(addr.sun_path)) {
printf("Socket path may not be longer than %lu characters\n",
sizeof(addr.sun_path) - 1);
return -1;
}
if (remove(socket_path) == -1 && errno != ENOENT) {
printf("Could not delete previous socket file entry at %s. Error: "
"%s\n", socket_path, strerror(errno));
return -1;
}
memset(&addr, 0, sizeof(struct sockaddr_un));
strcpy(addr.sun_path, socket_path);
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sfd == -1) {
printf("Could not create socket. Error: %s\n", strerror(errno));
return -1;
}
addr.sun_family = AF_UNIX;
if (bind(sfd, (struct sockaddr *) &addr,
sizeof(struct sockaddr_un)) == -1) {
printf("Could not bind socket. Error: %s\n", strerror(errno));
return -1;
}
if (listen(sfd, 1) == -1)
return -1;
printf("Awaiting connection on socket at %s...\n", socket_path);
int cfd = accept(sfd, NULL, NULL);
if (cfd == -1) {
printf("Could not accept connection. Error: %s\n",
strerror(errno));
return -1;
} else {
printf("Accepted connection!\n");
}
return cfd;
}
static ssize_t stream_writev(int fd, struct iovec *iov, int count,
void *userdata) {
(void)userdata;
ssize_t written = 0;
int cur = 0;
for (;;) {
written = writev(fd, iov+cur, count-cur);
if (written < 0)
return written;
while (cur < count && written >= iov[cur].iov_len)
written -= iov[cur++].iov_len;
if (cur == count)
break;
iov[cur].iov_base = (char *)iov[cur].iov_base + written;
iov[cur].iov_len -= written;
}
return written;
}
static ssize_t readall(int fd, void *buf, size_t len) {
size_t count = 0;
while (count < len) {
int i = read(fd, (char *)buf + count, len - count);
if (!i)
break;
if (i < 0)
return i;
count += i;
}
return count;
}
static ssize_t stream_read(int fd, void *buf, size_t buf_len, void *userdata) {
(void)userdata;
int res = readall(fd, buf, sizeof(struct fuse_in_header));
if (res == -1)
return res;
uint32_t packet_len = ((struct fuse_in_header *)buf)->len;
if (packet_len > buf_len)
return -1;
int prev_res = res;
res = readall(fd, (char *)buf + sizeof(struct fuse_in_header),
packet_len - sizeof(struct fuse_in_header));
return (res == -1) ? res : (res + prev_res);
}
static ssize_t stream_splice_send(int fdin, off_t *offin, int fdout,
off_t *offout, size_t len,
unsigned int flags, void *userdata) {
(void)userdata;
size_t count = 0;
while (count < len) {
int i = splice(fdin, offin, fdout, offout, len - count, flags);
if (i < 1)
return i;
count += i;
}
return count;
}
static void fuse_cmdline_help_uds(void)
{
printf(" -h --help print help\n"
" -V --version print version\n"
" -d -o debug enable debug output (implies -f)\n");
}
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
struct fuse_session *se;
struct fuse_cmdline_opts opts;
const struct fuse_custom_io io = {
.writev = stream_writev,
.read = stream_read,
.splice_receive = NULL,
.splice_send = stream_splice_send,
};
int cfd = -1;
int ret = -1;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
if (opts.show_help) {
printf("usage: %s [options]\n\n", argv[0]);
fuse_cmdline_help_uds();
fuse_lowlevel_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
printf("FUSE library version %s\n", fuse_pkgversion());
fuse_lowlevel_version();
ret = 0;
goto err_out1;
}
se = fuse_session_new(&args, &hello_ll_oper,
sizeof(hello_ll_oper), NULL);
if (se == NULL)
goto err_out1;
if (fuse_set_signal_handlers(se) != 0)
goto err_out2;
cfd = create_socket("/tmp/libfuse-hello-ll.sock");
if (cfd == -1)
goto err_out3;
if (fuse_session_custom_io(se, &io, cfd) != 0)
goto err_out3;
/* Block until ctrl+c */
ret = fuse_session_loop(se);
err_out3:
fuse_remove_signal_handlers(se);
err_out2:
fuse_session_destroy(se);
err_out1:
free(opts.mountpoint);
fuse_opt_free_args(&args);
return ret ? 1 : 0;
}
+3 -3
View File
@@ -1,7 +1,7 @@
examples = [ 'passthrough', 'passthrough_fh',
'hello', 'hello_ll', 'printcap',
'ioctl_client', 'poll_client', 'ioctl',
'cuse', 'cuse_client' ]
'hello', 'hello_ll', 'hello_ll_uds',
'printcap', 'ioctl_client', 'poll_client',
'ioctl', 'cuse', 'cuse_client' ]
if not platform.endswith('bsd') and platform != 'dragonfly'
examples += 'passthrough_ll'
+48 -9
View File
@@ -11,8 +11,8 @@
* This example implements a file system with a single file whose
* file name changes dynamically to reflect the current time.
*
* It illustrates the use of the fuse_lowlevel_notify_inval_entry()
* function.
* It illustrates the use of the fuse_lowlevel_notify_inval_entry() and
* fuse_lowlevel_notify_expire_entry() functions.
*
* To see the effect, first start the file system with the
* ``--no-notify``
@@ -64,6 +64,9 @@
* $ sleep 1; stat mnt/$file
* stat: cannot stat mnt/Time_is_20h_42m_11s: No such file or directory
*
* To use the function fuse_lowlevel_notify_expire_entry() instead of
* fuse_lowlevel_notify_inval_entry(), use the command line option --only-expire
*
* ## Compilation ##
*
* gcc -Wall notify_inval_entry.c `pkg-config fuse3 --cflags --libs` -o notify_inval_entry
@@ -82,7 +85,9 @@
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>
#include <stddef.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
@@ -90,17 +95,20 @@
static char file_name[MAX_STR_LEN];
static fuse_ino_t file_ino = 2;
static int lookup_cnt = 0;
static pthread_t main_thread;
/* Command line parsing */
struct options {
int no_notify;
float timeout;
int update_interval;
int only_expire;
};
static struct options options = {
.timeout = 5,
.no_notify = 0,
.update_interval = 1,
.only_expire = 0,
};
#define OPTION(t, p) \
@@ -109,6 +117,7 @@ static const struct fuse_opt option_spec[] = {
OPTION("--no-notify", no_notify),
OPTION("--update-interval=%d", update_interval),
OPTION("--timeout=%f", timeout),
OPTION("--only-expire", only_expire),
FUSE_OPT_END
};
@@ -247,12 +256,36 @@ static void* update_fs_loop(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
char *old_name;
while(1) {
while(!fuse_session_exited(se)) {
old_name = strdup(file_name);
update_fs();
if (!options.no_notify && lookup_cnt)
assert(fuse_lowlevel_notify_inval_entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
if (!options.no_notify && lookup_cnt) {
if(options.only_expire) { // expire entry
int ret = fuse_lowlevel_notify_expire_entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name));
// no kernel support
if (ret == -ENOSYS) {
printf("fuse_lowlevel_notify_expire_entry not supported by kernel\n");
printf("Exiting...\n");
fuse_session_exit(se);
// Make sure to exit now, rather than on next request from userspace
pthread_kill(main_thread, SIGPIPE);
break;
}
// 1) ret == 0: successful expire of an existing entry
// 2) ret == -ENOENT: kernel has already expired the entry /
// entry does not exist anymore in the kernel
assert(ret == 0 || ret == -ENOENT);
} else { // invalidate entry
assert(fuse_lowlevel_notify_inval_entry
(se, FUSE_ROOT_ID, old_name, strlen(old_name)) == 0);
}
}
free(old_name);
sleep(options.update_interval);
}
@@ -266,6 +299,7 @@ static void show_help(const char *progname)
" --timeout=<secs> Timeout for kernel caches\n"
" --update-interval=<secs> Update-rate of file system contents\n"
" --no-notify Disable kernel notifications\n"
" --only-expire Expire entries instead of invalidating them\n"
"\n");
}
@@ -299,7 +333,7 @@ int main(int argc, char *argv[]) {
update_fs();
se = fuse_session_new(&args, &tfs_oper,
sizeof(tfs_oper), NULL);
sizeof(tfs_oper), &se);
if (se == NULL)
goto err_out1;
@@ -311,6 +345,11 @@ int main(int argc, char *argv[]) {
fuse_daemonize(opts.foreground);
// Needed to ensure that the main thread continues/restarts processing as soon
// as the fuse session ends (immediately after calling fuse_session_exit() )
// and not only on the next request from userspace
main_thread = pthread_self();
/* Start thread to update file contents */
ret = pthread_create(&updater, NULL, update_fs_loop, (void *)se);
if (ret != 0) {
@@ -320,9 +359,9 @@ int main(int argc, char *argv[]) {
}
/* Block until ctrl+c or fusermount -u */
if (opts.singlethread)
if (opts.singlethread) {
ret = fuse_session_loop(se);
else {
} else {
config.clone_fd = opts.clone_fd;
config.max_idle_threads = opts.max_idle_threads;
ret = fuse_session_loop_mt(se, &config);
-4
View File
@@ -25,10 +25,6 @@
#define FUSE_USE_VERSION 31
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#ifdef linux
-4
View File
@@ -25,10 +25,6 @@
#define FUSE_USE_VERSION 31
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <fuse.h>
+143 -33
View File
@@ -43,11 +43,7 @@
* \include passthrough_hp.cc
*/
#define FUSE_USE_VERSION 35
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12)
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
@@ -82,6 +78,9 @@
using namespace std;
#define SFS_DEFAULT_THREADS "-1" // take libfuse value as default
#define SFS_DEFAULT_CLONE_FD "0"
/* We are re-using pointers to our `struct sfs_inode` and `struct
sfs_dirp` elements as inodes and file handles. This means that we
must be able to store pointer a pointer in both a fuse_ino_t
@@ -149,11 +148,16 @@ struct Fs {
Inode root;
double timeout;
bool debug;
bool debug_fuse;
bool foreground;
std::string source;
size_t blocksize;
dev_t src_dev;
bool nosplice;
bool nocache;
size_t num_threads;
bool clone_fd;
std::string fuse_mount_options;
};
static Fs fs{};
@@ -194,13 +198,19 @@ static void sfs_init(void *userdata, fuse_conn_info *conn) {
if (conn->capable & FUSE_CAP_FLOCK_LOCKS)
conn->want |= FUSE_CAP_FLOCK_LOCKS;
// Use splicing if supported. Since we are using writeback caching
// and readahead, individual requests should have a decent size so
// that splicing between fd's is well worth it.
if (conn->capable & FUSE_CAP_SPLICE_WRITE && !fs.nosplice)
conn->want |= FUSE_CAP_SPLICE_WRITE;
if (conn->capable & FUSE_CAP_SPLICE_READ && !fs.nosplice)
conn->want |= FUSE_CAP_SPLICE_READ;
if (fs.nosplice) {
// FUSE_CAP_SPLICE_READ is enabled in libfuse3 by default,
// see do_init() in in fuse_lowlevel.c
// Just unset both, in case FUSE_CAP_SPLICE_WRITE would also get enabled
// by default.
conn->want &= ~FUSE_CAP_SPLICE_READ;
conn->want &= ~FUSE_CAP_SPLICE_WRITE;
} else {
if (conn->capable & FUSE_CAP_SPLICE_WRITE)
conn->want |= FUSE_CAP_SPLICE_WRITE;
if (conn->capable & FUSE_CAP_SPLICE_READ)
conn->want |= FUSE_CAP_SPLICE_READ;
}
}
@@ -357,7 +367,14 @@ static int do_lookup(fuse_ino_t parent, const char *name,
cerr << "DEBUG: lookup(): inode " << e->attr.st_ino
<< " (userspace) already known; fd = " << inode.fd << endl;
lock_guard<mutex> g {inode.m};
inode.nlookup++;
if (fs.debug)
cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " "
<< "inode " << inode.src_ino
<< " count " << inode.nlookup << endl;
close(newfd);
} else { // no existing inode
/* This is just here to make Helgrind happy. It violates the
@@ -367,7 +384,13 @@ static int do_lookup(fuse_ino_t parent, const char *name,
lock_guard<mutex> g {inode.m};
inode.src_ino = e->attr.st_ino;
inode.src_dev = e->attr.st_dev;
inode.nlookup++;
if (fs.debug)
cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " "
<< "inode " << inode.src_ino
<< " count " << inode.nlookup << endl;
inode.fd = newfd;
fs_lock.unlock();
@@ -474,6 +497,10 @@ static void sfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
{
lock_guard<mutex> g {inode.m};
inode.nlookup++;
if (fs.debug)
cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " "
<< "inode " << inode.src_ino
<< " count " << inode.nlookup << endl;
}
fuse_reply_entry(req, &e);
@@ -529,6 +556,9 @@ static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
inode.generation++;
}
}
// decrease the ref which lookup above had increased
forget_one(e.ino, 1);
}
auto res = unlinkat(inode_p.fd, name, 0);
fuse_reply_err(req, res == -1 ? errno : 0);
@@ -545,6 +575,12 @@ static void forget_one(fuse_ino_t ino, uint64_t n) {
abort();
}
inode.nlookup -= n;
if (fs.debug)
cerr << "DEBUG:" << __func__ << ":" << __LINE__ << " "
<< "inode " << inode.src_ino
<< " count " << inode.nlookup << endl;
if (!inode.nlookup) {
if (fs.debug)
cerr << "DEBUG: forget: cleaning up inode " << inode.src_ino << endl;
@@ -656,7 +692,7 @@ static bool is_dot_or_dotdot(const char *name) {
static void do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, fuse_file_info *fi, int plus) {
off_t offset, fuse_file_info *fi, const int plus) {
auto d = get_dir_handle(fi);
Inode& inode = get_inode(ino);
lock_guard<mutex> g {inode.m};
@@ -701,28 +737,23 @@ static void do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
fuse_entry_param e{};
size_t entsize;
if(plus) {
if (plus) {
err = do_lookup(ino, entry->d_name, &e);
if (err)
goto error;
entsize = fuse_add_direntry_plus(req, p, rem, entry->d_name, &e, entry->d_off);
if (entsize > rem) {
if (fs.debug)
cerr << "DEBUG: readdir(): buffer full, returning data. " << endl;
forget_one(e.ino, 1);
break;
}
} else {
e.attr.st_ino = entry->d_ino;
e.attr.st_mode = entry->d_type << 12;
entsize = fuse_add_direntry(req, p, rem, entry->d_name, &e.attr, entry->d_off);
}
if (entsize > rem) {
if (fs.debug)
cerr << "DEBUG: readdir(): buffer full, returning data. " << endl;
break;
}
if (entsize > rem) {
if (fs.debug)
cerr << "DEBUG: readdir(): buffer full, returning data. " << endl;
if (plus)
forget_one(e.ino, 1);
break;
}
p += entsize;
@@ -856,6 +887,7 @@ static void sfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
lock_guard<mutex> g {inode.m};
inode.nopen++;
fi->keep_cache = (fs.timeout != 0);
fi->noflush = (fs.timeout == 0 && (fi->flags & O_ACCMODE) == O_RDONLY);
fi->fh = fd;
fuse_reply_open(req, fi);
}
@@ -1138,15 +1170,51 @@ static cxxopts::ParseResult parse_wrapper(cxxopts::Options& parser, int& argc, c
}
static void string_split(std::string s, std::vector<std::string>& out, std::string delimiter) {
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
std::string token;
while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) {
token = s.substr(pos_start, pos_end - pos_start);
pos_start = pos_end + delim_len;
out.push_back(token);
}
out.push_back(s.substr(pos_start));
}
static std::string string_join(const std::vector<std::string>& elems, char delim)
{
std::ostringstream out;
for (auto ii = elems.begin(); ii != elems.end(); ++ii) {
out << (*ii);
if (ii + 1 != elems.end()) {
out << delim;
}
}
return out.str();
}
static cxxopts::ParseResult parse_options(int argc, char **argv) {
cxxopts::Options opt_parser(argv[0]);
std::vector<std::string> mount_options;
opt_parser.add_options()
("debug", "Enable filesystem debug messages")
("debug-fuse", "Enable libfuse debug messages")
("foreground", "Run in foreground")
("help", "Print help")
("nocache", "Disable all caching")
("nosplice", "Do not use splice(2) to transfer data")
("single", "Run single-threaded");
("single", "Run single-threaded")
("o", "Mount options (see mount.fuse(5) - only use if you know what "
"you are doing)", cxxopts::value(mount_options))
("num-threads", "Number of libfuse worker threads",
cxxopts::value<int>()->default_value(SFS_DEFAULT_THREADS))
("clone-fd", "use separate fuse device fd for each thread",
cxxopts::value<bool>()->implicit_value(SFS_DEFAULT_CLONE_FD));
// FIXME: Find a better way to limit the try clause to just
// opt_parser.parse() (cf. https://github.com/jarro2783/cxxopts/issues/146)
@@ -1168,13 +1236,45 @@ static cxxopts::ParseResult parse_options(int argc, char **argv) {
}
fs.debug = options.count("debug") != 0;
fs.debug_fuse = options.count("debug-fuse") != 0;
fs.foreground = options.count("foreground") != 0;
if (fs.debug || fs.debug_fuse)
fs.foreground = true;
fs.nosplice = options.count("nosplice") != 0;
fs.num_threads = options["num-threads"].as<int>();
fs.clone_fd = options["clone-fd"].as<bool>();
char* resolved_path = realpath(argv[1], NULL);
if (resolved_path == NULL)
warn("WARNING: realpath() failed with");
fs.source = std::string {resolved_path};
free(resolved_path);
std::vector<std::string> flattened_mount_opts;
for (auto opt : mount_options) {
string_split(opt, flattened_mount_opts, ",");
}
bool found_fsname = false;
for (auto opt : flattened_mount_opts) {
if (opt.find("fsname=") == 0) {
found_fsname = true;
continue;
}
/* Filter out some obviously incorrect options. */
if (opt == "fd") {
std::cout << argv[0] << ": Unsupported mount option: " << opt << "\n";
print_usage(argv[0]);
exit(2);
}
}
if (!found_fsname) {
flattened_mount_opts.push_back("fsname=" + fs.source);
}
flattened_mount_opts.push_back("default_permissions");
fs.fuse_mount_options = string_join(flattened_mount_opts, ',');
return options;
}
@@ -1195,6 +1295,8 @@ static void maximize_fd_limit() {
int main(int argc, char *argv[]) {
struct fuse_loop_config *loop_config = NULL;
// Parse command line options
auto options {parse_options(argc, argv)};
@@ -1224,8 +1326,8 @@ int main(int argc, char *argv[]) {
fuse_args args = FUSE_ARGS_INIT(0, nullptr);
if (fuse_opt_add_arg(&args, argv[0]) ||
fuse_opt_add_arg(&args, "-o") ||
fuse_opt_add_arg(&args, "default_permissions,fsname=hpps") ||
(options.count("debug-fuse") && fuse_opt_add_arg(&args, "-odebug")))
fuse_opt_add_arg(&args, fs.fuse_mount_options.c_str()) ||
(fs.debug_fuse && fuse_opt_add_arg(&args, "-odebug")))
errx(3, "ERROR: Out of memory");
fuse_lowlevel_ops sfs_oper {};
@@ -1241,15 +1343,21 @@ int main(int argc, char *argv[]) {
umask(0);
// Mount and run main loop
struct fuse_loop_config loop_config;
loop_config.clone_fd = 0;
loop_config.max_idle_threads = 10;
loop_config = fuse_loop_cfg_create();
if (fs.num_threads != -1)
fuse_loop_cfg_set_idle_threads(loop_config, fs.num_threads);
if (fuse_session_mount(se, argv[2]) != 0)
goto err_out3;
fuse_daemonize(fs.foreground);
if (options.count("single"))
ret = fuse_session_loop(se);
else
ret = fuse_session_loop_mt(se, &loop_config);
ret = fuse_session_loop_mt(se, loop_config);
fuse_session_unmount(se);
@@ -1258,6 +1366,8 @@ err_out3:
err_out2:
fuse_session_destroy(se);
err_out1:
fuse_loop_cfg_destroy(loop_config);
fuse_opt_free_args(&args);
return ret ? 1 : 0;
+25 -4
View File
@@ -37,8 +37,6 @@
#define _GNU_SOURCE
#define FUSE_USE_VERSION 34
#include "config.h"
#include <fuse_lowlevel.h>
#include <unistd.h>
#include <stdlib.h>
@@ -91,7 +89,7 @@ struct lo_data {
int writeback;
int flock;
int xattr;
const char *source;
char *source;
double timeout;
int cache;
int timeout_set;
@@ -127,6 +125,23 @@ static const struct fuse_opt lo_opts[] = {
FUSE_OPT_END
};
static void passthrough_ll_help(void)
{
printf(
" -o writeback Enable writeback\n"
" -o no_writeback Disable write back\n"
" -o source=/home/dir Source directory to be mounted\n"
" -o flock Enable flock\n"
" -o no_flock Disable flock\n"
" -o xattr Enable xattr\n"
" -o no_xattr Disable xattr\n"
" -o timeout=1.0 Caching timeout\n"
" -o timeout=0/1 Timeout is set\n"
" -o cache=never Disable cache\n"
" -o cache=auto Auto enable cache\n"
" -o cache=always Cache always\n");
}
static struct lo_data *lo_data(fuse_req_t req)
{
return (struct lo_data *) fuse_req_userdata(req);
@@ -1187,6 +1202,7 @@ int main(int argc, char *argv[])
printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
fuse_cmdline_help();
fuse_lowlevel_help();
passthrough_ll_help();
ret = 0;
goto err_out1;
} else if (opts.show_version) {
@@ -1224,7 +1240,11 @@ int main(int argc, char *argv[])
}
} else {
lo.source = "/";
lo.source = strdup("/");
if(!lo.source) {
fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
exit(1);
}
}
if (!lo.timeout_set) {
switch (lo.cache) {
@@ -1286,5 +1306,6 @@ err_out1:
if (lo.root.fd >= 0)
close(lo.root.fd);
free(lo.source);
return ret ? 1 : 0;
}
+1 -3
View File
@@ -23,8 +23,6 @@
#define FUSE_USE_VERSION 31
#include <config.h>
#include <fuse.h>
#include <unistd.h>
#include <ctype.h>
@@ -46,7 +44,7 @@ static unsigned fsel_open_mask;
static const char fsel_hex_map[] = "0123456789ABCDEF";
static struct fuse *fsel_fuse; /* needed for poll notification */
#define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */
#define FSEL_CNT_MAX 10 /* each file can store up to 10 chars */
#define FSEL_FILES 16
static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
+1 -1
View File
@@ -19,7 +19,7 @@
* \include poll_client.c
*/
#include <config.h>
#include <fuse_config.h>
#include <sys/select.h>
#include <sys/time.h>
+2 -4
View File
@@ -21,8 +21,6 @@
#define FUSE_USE_VERSION 31
#include <config.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <unistd.h>
@@ -39,8 +37,6 @@ static void pc_init(void *userdata,
printf("Protocol version: %d.%d\n", conn->proto_major,
conn->proto_minor);
printf("Capabilities:\n");
if(conn->capable & FUSE_CAP_WRITEBACK_CACHE)
printf("\tFUSE_CAP_WRITEBACK_CACHE\n");
if(conn->capable & FUSE_CAP_ASYNC_READ)
printf("\tFUSE_CAP_ASYNC_READ\n");
if(conn->capable & FUSE_CAP_POSIX_LOCKS)
@@ -83,6 +79,8 @@ static void pc_init(void *userdata,
printf("\tFUSE_CAP_NO_OPENDIR_SUPPORT\n");
if(conn->capable & FUSE_CAP_EXPLICIT_INVAL_DATA)
printf("\tFUSE_CAP_EXPLICIT_INVAL_DATA\n");
if(conn->capable & FUSE_CAP_EXPIRE_ONLY)
printf("\tFUSE_CAP_EXPIRE_ONLY\n");
fuse_session_exit(se);
}
+51 -1
View File
@@ -247,6 +247,14 @@ struct fuse_config {
*/
int auto_cache;
/**
* By default, fuse waits for all pending writes to complete
* and calls the FLUSH operation on close(2) of every fuse fd.
* With this option, wait and FLUSH are not done for read-only
* fuse fd, similar to the behavior of NFS/SMB clients.
*/
int no_rofd_flush;
/**
* The timeout in seconds for which file attributes are cached
* for the purpose of checking if auto_cache should flush the
@@ -266,6 +274,20 @@ struct fuse_config {
* fuse_file_info argument is NULL.
*/
int nullpath_ok;
/**
* Allow parallel direct-io writes to operate on the same file.
*
* FUSE implementations which do not handle parallel writes on
* same file/region should NOT enable this option at all as it
* might lead to data inconsistencies.
*
* For the FUSE implementations which have their own mechanism
* of cache/data integrity are beneficiaries of this setting as
* it now open doors to parallel writes on the same file (without
* enabling this setting, all direct writes on the same file are
* serialized, resulting in huge data bandwidth loss).
*/
int parallel_direct_writes;
/**
* The remaining options are used by libfuse internally and
@@ -555,15 +577,28 @@ struct fuse_operations {
* passes non-zero offset to the filler function. When the buffer
* is full (or an error happens) the filler function will return
* '1'.
*
* When FUSE_READDIR_PLUS is not set, only some parameters of the
* fill function (the fuse_fill_dir_t parameter) are actually used:
* The file type (which is part of stat::st_mode) is used. And if
* fuse_config::use_ino is set, the inode (stat::st_ino) is also
* used. The other fields are ignored when FUSE_READDIR_PLUS is not
* set.
*/
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
struct fuse_file_info *, enum fuse_readdir_flags);
/** Release directory
*
* If the directory has been removed after the call to opendir, the
* path parameter will be NULL.
*/
int (*releasedir) (const char *, struct fuse_file_info *);
/** Synchronize directory contents
*
* If the directory has been removed after the call to opendir, the
* path parameter will be NULL.
*
* If the datasync parameter is non-zero, then only the user data
* should be flushed, not the meta data
@@ -927,8 +962,15 @@ struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *o
size_t op_size, void *private_data);
#define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data)
#else
#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op,
size_t op_size, void *private_data);
#else /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
struct fuse *fuse_new_31(struct fuse_args *args,
const struct fuse_operations *op,
size_t op_size, void *private_data);
#define fuse_new(args, op, size, data) fuse_new_31(args, op, size, data)
#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
#endif
/**
@@ -990,6 +1032,9 @@ void fuse_exit(struct fuse *f);
#if FUSE_USE_VERSION < 32
int fuse_loop_mt_31(struct fuse *f, int clone_fd);
#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd)
#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
#define fuse_loop_mt(f, config) fuse_loop_mt_32(f, config)
#else
/**
* FUSE event loop with multiple threads
@@ -1017,14 +1062,19 @@ int fuse_loop_mt_31(struct fuse *f, int clone_fd);
* in the callback function of fuse_operations is also thread-safe.
*
* @param f the FUSE handle
* @param config loop configuration
* @param config loop configuration, may be NULL and defaults will be used then
* @return see fuse_session_loop()
*
* See also: fuse_loop()
*/
#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config);
#else
#define fuse_loop_mt(f, config) fuse_loop_mt_312(f, config)
#endif /* LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS */
#endif
/**
* Get the current context
*
+93 -8
View File
@@ -14,6 +14,12 @@
#ifndef FUSE_COMMON_H_
#define FUSE_COMMON_H_
#ifdef HAVE_LIBFUSE_PRIVATE_CONFIG_H
#include "fuse_config.h"
#endif
#include "libfuse_config.h"
#include "fuse_opt.h"
#include "fuse_log.h"
#include <stdint.h>
@@ -23,7 +29,7 @@
#define FUSE_MAJOR_VERSION 3
/** Minor version of FUSE library interface */
#define FUSE_MINOR_VERSION 10
#define FUSE_MINOR_VERSION 16
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min))
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
@@ -53,16 +59,19 @@ struct fuse_file_info {
requests if write caching had been disabled. */
unsigned int writepage : 1;
/** Can be filled in by open, to use direct I/O on this file. */
/** Can be filled in by open/create, to use direct I/O on this file. */
unsigned int direct_io : 1;
/** Can be filled in by open. It signals the kernel that any
currently cached file data (ie., data that the filesystem
provided the last time the file was open) need not be
invalidated. Has no effect when set in other contexts (in
particular it does nothing when set by opendir()). */
/** Can be filled in by open and opendir. It signals the kernel that any
currently cached data (ie., data that the filesystem provided the
last time the file/directory was open) need not be invalidated when
the file/directory is closed. */
unsigned int keep_cache : 1;
/** Can be filled by open/create, to allow parallel direct writes on this
* file */
unsigned int parallel_direct_writes : 1;
/** Indicates a flush operation. Set in flush operation, also
maybe set in highlevel lock operation and lowlevel release
operation. */
@@ -83,8 +92,12 @@ struct fuse_file_info {
nothing when set by open()). */
unsigned int cache_readdir : 1;
/** Can be filled in by open, to indicate that flush is not needed
on close. */
unsigned int noflush : 1;
/** Padding. Reserved for future use*/
unsigned int padding : 25;
unsigned int padding : 23;
unsigned int padding2 : 32;
/** File handle id. May be filled in by filesystem in create,
@@ -100,11 +113,20 @@ struct fuse_file_info {
uint32_t poll_events;
};
/**
* Configuration parameters passed to fuse_session_loop_mt() and
* fuse_loop_mt().
* Deprecated and replaced by a newer private struct in FUSE API
* version 312 (FUSE_MAKE_VERSION(3, 12)
*/
#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
struct fuse_loop_config_v1; /* forward declarition */
struct fuse_loop_config {
#else
struct fuse_loop_config_v1 {
#endif
/**
* whether to use separate device fds for each thread
* (may increase performance)
@@ -124,6 +146,7 @@ struct fuse_loop_config {
unsigned int max_idle_threads;
};
/**************************************************************************
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' *
**************************************************************************/
@@ -394,6 +417,29 @@ struct fuse_loop_config {
*/
#define FUSE_CAP_EXPLICIT_INVAL_DATA (1 << 25)
/**
* Indicates support that dentries can be expired.
*
* Expiring dentries, instead of invalidating them, makes a difference for
* overmounted dentries, where plain invalidation would detach all submounts
* before dropping the dentry from the cache. If only expiry is set on the
* dentry, then any overmounts are left alone and until ->d_revalidate()
* is called.
*
* Note: ->d_revalidate() is not called for the case of following a submount,
* so invalidation will only be triggered for the non-overmounted case.
* The dentry could also be mounted in a different mount instance, in which case
* any submounts will still be detached.
*/
#define FUSE_CAP_EXPIRE_ONLY (1 << 26)
/**
* Indicates that an extended 'struct fuse_setxattr' is used by the kernel
* side - extra_flags are passed, which are used (as of now by acl) processing.
* For example FUSE_SETXATTR_ACL_KILL_SGID might be set.
*/
#define FUSE_CAP_SETXATTR_EXT (1 << 27)
/**
* Ioctl flags
*
@@ -830,6 +876,45 @@ int fuse_set_signal_handlers(struct fuse_session *se);
*/
void fuse_remove_signal_handlers(struct fuse_session *se);
/**
* Create and set default config for fuse_session_loop_mt and fuse_loop_mt.
*
* @return anonymous config struct
*/
struct fuse_loop_config *fuse_loop_cfg_create(void);
/**
* Free the config data structure
*/
void fuse_loop_cfg_destroy(struct fuse_loop_config *config);
/**
* fuse_loop_config setter to set the number of max idle threads.
*/
void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
unsigned int value);
/**
* fuse_loop_config setter to set the number of max threads.
*/
void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
unsigned int value);
/**
* fuse_loop_config setter to enable the clone_fd feature
*/
void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
unsigned int value);
/**
* Convert old config to more recernt fuse_loop_config2
*
* @param config current config2 type
* @param v1_conf older config1 type (below FUSE API 312)
*/
void fuse_loop_cfg_convert(struct fuse_loop_config *config,
struct fuse_loop_config_v1 *v1_conf);
/* ----------------------------------------------------------- *
* Compatibility stuff *
* ----------------------------------------------------------- */
+9 -7
View File
@@ -1,5 +1,9 @@
#ifndef LIBFUSE_CONFIG_H
#define LIBFUSE_CONFIG_H
/*
* Autogenerated by the Meson build system.
* Do not edit, your changes will be lost.
*/
#pragma once
#define HAVE_COPY_FILE_RANGE
@@ -33,12 +37,10 @@
#define HAVE_VMSPLICE
#define PACKAGE_VERSION "3.10.5"
#define PACKAGE_VERSION "3.16.1"
#define pthread_setcancelstate(state, p)
#define pthread_setcancelstate(state,p)
#define pthread_cancel(thread)
#define PTHREAD_CANCEL_ENABLE 0
#define PTHREAD_CANCEL_DISABLE 0
#endif
#define PTHREAD_CANCEL_DISABLE 0
+260 -15
View File
@@ -38,6 +38,43 @@
*
* Protocol changelog:
*
* 7.1:
* - add the following messages:
* FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK,
* FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE,
* FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR,
* FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR,
* FUSE_RELEASEDIR
* - add padding to messages to accommodate 32-bit servers on 64-bit kernels
*
* 7.2:
* - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags
* - add FUSE_FSYNCDIR message
*
* 7.3:
* - add FUSE_ACCESS message
* - add FUSE_CREATE message
* - add filehandle to fuse_setattr_in
*
* 7.4:
* - add frsize to fuse_kstatfs
* - clean up request size limit checking
*
* 7.5:
* - add flags and max_write to fuse_init_out
*
* 7.6:
* - add max_readahead to fuse_init_in and fuse_init_out
*
* 7.7:
* - add FUSE_INTERRUPT message
* - add POSIX file lock support
*
* 7.8:
* - add lock_owner and flags fields to fuse_release_in
* - add FUSE_BMAP message
* - add FUSE_DESTROY message
*
* 7.9:
* - new fuse_getattr_in input argument of GETATTR
* - add lk_flags in fuse_lk_in
@@ -133,6 +170,42 @@
*
* 7.31
* - add FUSE_WRITE_KILL_PRIV flag
* - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING
* - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag
*
* 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
*
* 7.38
* - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
* - add FOPEN_PARALLEL_DIRECT_WRITES
* - add total_extlen to fuse_in_header
* - add FUSE_MAX_NR_SECCTX
* - add extension header
* - add FUSE_EXT_GROUPS
* - add FUSE_CREATE_SUPP_GROUP
*/
#ifndef _LINUX_FUSE_H
@@ -168,7 +241,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 31
#define FUSE_KERNEL_MINOR_VERSION 38
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -192,7 +265,7 @@ struct fuse_attr {
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t padding;
uint32_t flags;
};
struct fuse_kstatfs {
@@ -229,6 +302,7 @@ struct fuse_file_lock {
#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
@@ -238,12 +312,16 @@ 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)
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
*/
#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)
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
/**
* INIT request/reply flags
@@ -274,6 +352,23 @@ 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: 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
* FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir,
* symlink and mknod (single group that matches parent)
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -301,6 +396,16 @@ struct fuse_file_lock {
#define FUSE_CACHE_SYMLINKS (1 << 23)
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
#define FUSE_MAP_ALIGNMENT (1 << 26)
#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)
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
/**
* CUSE INIT request/reply flags
@@ -330,11 +435,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
@@ -376,6 +484,44 @@ 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)
/**
* notify_inval_entry flags
* FUSE_EXPIRE_ONLY
*/
#define FUSE_EXPIRE_ONLY (1 << 0)
/**
* extension type
* FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx
* FUSE_EXT_GROUPS: &fuse_supp_groups extension
*/
enum fuse_ext_type {
/* Types 0..31 are reserved for fuse_secctx_header */
FUSE_MAX_NR_SECCTX = 31,
FUSE_EXT_GROUPS = 32,
};
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -422,9 +568,17 @@ enum fuse_opcode {
FUSE_RENAME2 = 45,
FUSE_LSEEK = 46,
FUSE_COPY_FILE_RANGE = 47,
FUSE_SETUPMAPPING = 48,
FUSE_REMOVEMAPPING = 49,
FUSE_SYNCFS = 50,
FUSE_TMPFILE = 51,
/* CUSE specific operations */
CUSE_INIT = 4096
CUSE_INIT = 4096,
/* Reserved opcodes: helpful to detect structure endian-ness */
CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */
FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */
};
enum fuse_notify_code {
@@ -434,7 +588,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_STORE = 4,
FUSE_NOTIFY_RETRIEVE = 5,
FUSE_NOTIFY_DELETE = 6,
FUSE_NOTIFY_CODE_MAX
FUSE_NOTIFY_CODE_MAX,
};
/* The read buffer is required to be at least 8k, but may be much larger */
@@ -531,14 +685,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 {
@@ -600,9 +754,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 {
@@ -637,6 +795,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
@@ -652,8 +812,9 @@ struct fuse_init_out {
uint32_t max_write;
uint32_t time_gran;
uint16_t max_pages;
uint16_t padding;
uint32_t unused[8];
uint16_t map_alignment;
uint32_t flags2;
uint32_t unused[7];
};
#define CUSE_INIT_INFO_MAX 4096
@@ -744,7 +905,8 @@ struct fuse_in_header {
uint32_t uid;
uint32_t gid;
uint32_t pid;
uint32_t padding;
uint16_t total_extlen; /* length of extensions in 8byte units */
uint16_t padding;
};
struct fuse_out_header {
@@ -761,9 +923,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)
@@ -786,7 +951,7 @@ struct fuse_notify_inval_inode_out {
struct fuse_notify_inval_entry_out {
uint64_t parent;
uint32_t namelen;
uint32_t padding;
uint32_t flags;
};
struct fuse_notify_delete_out {
@@ -822,7 +987,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;
@@ -845,4 +1011,83 @@ 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;
};
/**
* struct fuse_ext_header - extension header
* @size: total size of this extension including this header
* @type: type of extension
*
* This is made compatible with fuse_secctx_header by using type values >
* FUSE_MAX_NR_SECCTX
*/
struct fuse_ext_header {
uint32_t size;
uint32_t type;
};
/**
* struct fuse_supp_groups - Supplementary group extension
* @nr_groups: number of supplementary groups
* @groups: flexible array of group IDs
*/
struct fuse_supp_groups {
uint32_t nr_groups;
uint32_t groups[];
};
#endif /* _LINUX_FUSE_H */
+149 -27
View File
@@ -127,6 +127,27 @@ struct fuse_forget_data {
uint64_t nlookup;
};
struct fuse_custom_io {
ssize_t (*writev)(int fd, struct iovec *iov, int count, void *userdata);
ssize_t (*read)(int fd, void *buf, size_t buf_len, void *userdata);
ssize_t (*splice_receive)(int fdin, off_t *offin, int fdout,
off_t *offout, size_t len,
unsigned int flags, void *userdata);
ssize_t (*splice_send)(int fdin, off_t *offin, int fdout,
off_t *offout, size_t len,
unsigned int flags, void *userdata);
};
/**
* Flags for fuse_lowlevel_notify_entry()
* 0 = invalidate entry
* FUSE_LL_EXPIRE_ONLY = expire entry
*/
enum fuse_notify_entry_flags {
FUSE_LL_INVALIDATE = 0,
FUSE_LL_EXPIRE_ONLY = (1 << 0),
};
/* 'to_set' flags in setattr */
#define FUSE_SET_ATTR_MODE (1 << 0)
#define FUSE_SET_ATTR_UID (1 << 1)
@@ -136,7 +157,15 @@ struct fuse_forget_data {
#define FUSE_SET_ATTR_MTIME (1 << 5)
#define FUSE_SET_ATTR_ATIME_NOW (1 << 7)
#define FUSE_SET_ATTR_MTIME_NOW (1 << 8)
#define FUSE_SET_ATTR_FORCE (1 << 9)
#define FUSE_SET_ATTR_CTIME (1 << 10)
#define FUSE_SET_ATTR_KILL_SUID (1 << 11)
#define FUSE_SET_ATTR_KILL_SGID (1 << 12)
#define FUSE_SET_ATTR_FILE (1 << 13)
#define FUSE_SET_ATTR_KILL_PRIV (1 << 14)
#define FUSE_SET_ATTR_OPEN (1 << 15)
#define FUSE_SET_ATTR_TIMES_SET (1 << 16)
#define FUSE_SET_ATTR_TOUCH (1 << 17)
/* ----------------------------------------------------------- *
* Request methods and replies *
@@ -285,6 +314,12 @@ struct fuse_lowlevel_ops {
* expected to reset the setuid and setgid bits if the file
* size or owner is being changed.
*
* This method will not be called to update st_atime or st_ctime implicitly
* (eg. after a read() request), and only be called to implicitly update st_mtime
* if writeback caching is active. It is the filesystem's responsibility to update
* these timestamps when needed, and (if desired) to implement mount options like
* `noatime` or `relatime`.
*
* If the setattr was invoked from the ftruncate() system call
* under Linux kernel versions 2.6.15 or later, the fi->fh will
* contain the value set by the open method or will be undefined
@@ -1648,11 +1683,10 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
off_t off, off_t len);
/**
* Notify to invalidate parent attributes and the dentry matching
* parent/name
* Notify to invalidate parent attributes and the dentry matching parent/name
*
* To avoid a deadlock this function must not be called in the
* execution path of a related filesytem operation or within any code
* execution path of a related filesystem operation or within any code
* that could hold a lock that could be needed to execute such an
* operation. As of kernel 4.18, a "related operation" is a lookup(),
* symlink(), mknod(), mkdir(), unlink(), rename(), link() or create()
@@ -1675,6 +1709,37 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen);
/**
* Notify to expire parent attributes and the dentry matching parent/name
*
* Same restrictions apply as for fuse_lowlevel_notify_inval_entry()
*
* Compared to invalidating an entry, expiring the entry results not in a
* forceful removal of that entry from kernel cache but instead the next access
* to it forces a lookup from the filesystem.
*
* This makes a difference for overmounted dentries, where plain invalidation
* would detach all submounts before dropping the dentry from the cache.
* If only expiry is set on the dentry, then any overmounts are left alone and
* until ->d_revalidate() is called.
*
* Note: ->d_revalidate() is not called for the case of following a submount,
* so invalidation will only be triggered for the non-overmounted case.
* The dentry could also be mounted in a different mount instance, in which case
* any submounts will still be detached.
*
* Added in FUSE protocol version 7.38. If the kernel does not support
* this (or a newer) version, the function will return -ENOSYS and do nothing.
*
* @param se the session object
* @param parent inode number
* @param name file name
* @param namelen strlen() of file name
* @return zero for success, -errno for failure, -enosys if no kernel support
*/
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen);
/**
* This function behaves like fuse_lowlevel_notify_inval_entry() with
* the following additional effect (at least as of Linux kernel 4.8):
@@ -1685,7 +1750,7 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
* that the dentry has been deleted.
*
* To avoid a deadlock this function must not be called while
* executing a related filesytem operation or while holding a lock
* executing a related filesystem operation or while holding a lock
* that could be needed to execute such an operation (see the
* description of fuse_lowlevel_notify_inval_entry() for more
* details).
@@ -1868,6 +1933,11 @@ void fuse_cmdline_help(void);
* Filesystem setup & teardown *
* ----------------------------------------------------------- */
/**
* Note: Any addition to this struct needs to create a compatibility symbol
* for fuse_parse_cmdline(). For ABI compatibility reasons it is also
* not possible to remove struct members.
*/
struct fuse_cmdline_opts {
int singlethread;
int foreground;
@@ -1877,7 +1947,11 @@ struct fuse_cmdline_opts {
int show_version;
int show_help;
int clone_fd;
unsigned int max_idle_threads;
unsigned int max_idle_threads; /* discouraged, due to thread
* destruct overhead */
/* Added in libfuse-3.12 */
unsigned int max_threads;
};
/**
@@ -1898,8 +1972,20 @@ struct fuse_cmdline_opts {
* @param opts output argument for parsed options
* @return 0 on success, -1 on failure
*/
#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
int fuse_parse_cmdline(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
#else
#if FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
int fuse_parse_cmdline_30(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_30(args, opts)
#else
int fuse_parse_cmdline_312(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
#define fuse_parse_cmdline(args, opts) fuse_parse_cmdline_312(args, opts)
#endif
#endif
/**
* Create a low level session.
@@ -1933,6 +2019,36 @@ struct fuse_session *fuse_session_new(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
/**
* Set a file descriptor for the session.
*
* This function can be used if you want to have a custom communication
* interface instead of using a mountpoint. In practice, this means that instead
* of calling fuse_session_mount() and fuse_session_unmount(), one could call
* fuse_session_custom_io() where fuse_session_mount() would have otherwise been
* called.
*
* In `io`, implementations for read and writev MUST be provided. Otherwise -1
* will be returned and `fd` will not be used. Implementations for `splice_send`
* and `splice_receive` are optional. If they are not provided splice will not
* be used for send or receive respectively.
*
* The provided file descriptor `fd` will be closed when fuse_session_destroy()
* is called.
*
* @param se session object
* @param io Custom io to use when retrieving/sending requests/responses
* @param fd file descriptor for the session
*
* @return 0 on success
* @return -EINVAL if `io`, `io->read` or `ìo->writev` are NULL
* @return -EBADF if `fd` was smaller than 0
* @return -errno if failed to allocate memory to store `io`
*
**/
int fuse_session_custom_io(struct fuse_session *se,
const struct fuse_custom_io *io, int fd);
/**
* Mount a FUSE file system.
*
@@ -1968,34 +2084,40 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint);
int fuse_session_loop(struct fuse_session *se);
#if FUSE_USE_VERSION < 32
int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd)
#elif FUSE_USE_VERSION < FUSE_MAKE_VERSION(3, 12)
int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
#define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
#else
#if (!defined(__UCLIBC__) && !defined(__APPLE__))
/**
* Enter a multi-threaded event loop.
*
* For a description of the return value and the conditions when the
* event loop exits, refer to the documentation of
* fuse_session_loop().
*
* @param se the session
* @param config session loop configuration
* @return see fuse_session_loop()
*/
int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
#else
int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
#define fuse_session_loop_mt(se, config) fuse_session_loop_mt_32(se, config)
#endif
#if (defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
/**
* Enter a multi-threaded event loop.
*
* For a description of the return value and the conditions when the
* event loop exits, refer to the documentation of
* fuse_session_loop().
*
* @param se the session
* @param config session loop configuration
* @return see fuse_session_loop()
*/
int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config);
#else
int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
#define fuse_session_loop_mt(se, config) fuse_session_loop_mt_312(se, config)
#endif
#endif
/**
* Flag a session as terminated.
*
* This function is invoked by the POSIX signal handlers, when
* registered using fuse_set_signal_handlers(). It will cause any
* running event loops to terminate on the next opportunity.
* This will cause any running event loops to terminate on the next opportunity. If this function is
* called by a thread that is not a FUSE worker thread, the next
* opportunity will be when FUSE a request is received (which may be far in the future if the
* filesystem is not currently being used by any clients). One way to avoid this delay is to
* afterwards sent a signal to the main thread (if fuse_set_signal_handlers() is used, SIGPIPE
* will cause the main thread to wake-up but otherwise be ignored).
*
* @param se the session
*/
+42
View File
@@ -0,0 +1,42 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2023 Giulio Benetti <giulio.benetti@benettiengineering.com>
Logging API.
This program can be distributed under the terms of the GNU LGPLv2.
See the file LICENSE
*/
#ifndef FUSE_MOUNT_COMPAT_H_
#define FUSE_MOUNT_COMPAT_H_
#include <sys/mount.h>
/* Some libc don't define MS_*, so define them manually
* (values taken from https://elixir.bootlin.com/linux/v4.0.9/source/include/uapi/linux/fs.h#L68 on)
*/
#ifndef MS_DIRSYNC
#define MS_DIRSYNC 128
#endif
#ifndef MS_REC
#define MS_REC 16384
#endif
#ifndef MS_PRIVATE
#define MS_PRIVATE (1<<18)
#endif
#ifndef MS_LAZYTIME
#define MS_LAZYTIME (1<<25)
#endif
#ifndef UMOUNT_DETACH
#define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
#endif
#ifndef UMOUNT_NOFOLLOW
#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
#endif
#ifndef UMOUNT_UNUSED
#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
#endif
#endif /* FUSE_MOUNT_COMPAT_H_ */
+9
View File
@@ -0,0 +1,9 @@
/*
* Autogenerated by the Meson build system.
* Do not edit, your changes will be lost.
*/
#pragma once
#define LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS 1
+1 -1
View File
@@ -11,7 +11,7 @@
#define _GNU_SOURCE
#include "config.h"
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include <string.h>
+56
View File
@@ -0,0 +1,56 @@
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Helper functions to create (simple) standalone programs. With the
aid of these functions it should be possible to create full FUSE
file system by implementing nothing but the request handlers.
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
*/
/* Description:
This file has compatibility symbols for platforms that do not
support version symboling
*/
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_opt.h"
#include "fuse_lowlevel.h"
#include "mount_util.h"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <sys/param.h>
/**
* Compatibility ABI symbol for systems that do not support version symboling
*/
#if (!defined(LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS))
/* With current libfuse fuse_parse_cmdline is a macro pointing to the
* versioned function. Here in this file we need to provide the ABI symbol
* and the redirecting macro is conflicting.
*/
#ifdef fuse_parse_cmdline
#undef fuse_parse_cmdline
#endif
int fuse_parse_cmdline_30(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
int fuse_parse_cmdline(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
int fuse_parse_cmdline(struct fuse_args *args,
struct fuse_cmdline_opts *opts)
{
return fuse_parse_cmdline_30(args, opts);
}
#endif
+4 -5
View File
@@ -7,7 +7,7 @@
See the file COPYING.LIB.
*/
#include "config.h"
#include "fuse_config.h"
#include "cuse_lowlevel.h"
#include "fuse_kernel.h"
#include "fuse_i.h"
@@ -351,10 +351,9 @@ int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci,
return 1;
if (multithreaded) {
struct fuse_loop_config config;
config.clone_fd = 0;
config.max_idle_threads = 10;
res = fuse_session_loop_mt_32(se, &config);
struct fuse_loop_config *config = fuse_loop_cfg_create();
res = fuse_session_loop_mt(se, config);
fuse_loop_cfg_destroy(config);
}
else
res = fuse_session_loop(se);
+98 -96
View File
@@ -9,11 +9,7 @@
See the file COPYING.LIB
*/
/* For pthread_rwlock_t */
#define _GNU_SOURCE
#include "config.h"
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_lowlevel.h"
#include "fuse_opt.h"
@@ -59,7 +55,6 @@
struct fuse_fs {
struct fuse_operations op;
struct fuse_module *m;
void *user_data;
int debug;
};
@@ -81,8 +76,6 @@ struct lock_queue_element {
char **path2;
struct node **wnode2;
int err;
bool first_locked : 1;
bool second_locked : 1;
bool done : 1;
};
@@ -252,7 +245,7 @@ static int fuse_load_so_module(const char *module)
int ret = -1;
char *tmp;
struct fusemod_so *so;
fuse_module_factory_t factory;
fuse_module_factory_t *factory;
tmp = malloc(strlen(module) + 64);
if (!tmp) {
@@ -274,13 +267,13 @@ static int fuse_load_so_module(const char *module)
}
sprintf(tmp, "fuse_module_%s_factory", module);
*(void**)(&factory) = dlsym(so->handle, tmp);
factory = (fuse_module_factory_t*)dlsym(so->handle, tmp);
if (factory == NULL) {
fuse_log(FUSE_LOG_ERR, "fuse: symbol <%s> not found in module: %s\n",
tmp, dlerror());
goto out_dlclose;
}
ret = fuse_register_module(module, factory, so);
ret = fuse_register_module(module, *factory, so);
if (ret)
goto out_dlclose;
@@ -1037,7 +1030,7 @@ static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
for (node = get_node(f, nodeid); node->nodeid != FUSE_ROOT_ID;
node = node->parent) {
err = -ENOENT;
err = -ESTALE;
if (node->name == NULL || node->parent == NULL)
goto out_unlock;
@@ -1076,26 +1069,30 @@ static int try_get_path(struct fuse *f, fuse_ino_t nodeid, const char *name,
return err;
}
static void queue_element_unlock(struct fuse *f, struct lock_queue_element *qe)
static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
fuse_ino_t nodeid2, const char *name2,
char **path1, char **path2,
struct node **wnode1, struct node **wnode2)
{
struct node *wnode;
int err;
if (qe->first_locked) {
wnode = qe->wnode1 ? *qe->wnode1 : NULL;
unlock_path(f, qe->nodeid1, wnode, NULL);
qe->first_locked = false;
}
if (qe->second_locked) {
wnode = qe->wnode2 ? *qe->wnode2 : NULL;
unlock_path(f, qe->nodeid2, wnode, NULL);
qe->second_locked = false;
/* FIXME: locking two paths needs deadlock checking */
err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
if (!err) {
err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
if (err) {
struct node *wn1 = wnode1 ? *wnode1 : NULL;
unlock_path(f, nodeid1, wn1, NULL);
free(*path1);
}
}
return err;
}
static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
{
int err;
bool first = (qe == f->lockq);
if (!qe->path1) {
/* Just waiting for it to be unlocked */
@@ -1105,44 +1102,21 @@ static void queue_element_wakeup(struct fuse *f, struct lock_queue_element *qe)
return;
}
if (!qe->first_locked) {
if (qe->done)
return; // Don't try to double-lock the element
if (!qe->path2) {
err = try_get_path(f, qe->nodeid1, qe->name1, qe->path1,
qe->wnode1, true);
if (!err)
qe->first_locked = true;
else if (err != -EAGAIN)
goto err_unlock;
}
if (!qe->second_locked && qe->path2) {
err = try_get_path(f, qe->nodeid2, qe->name2, qe->path2,
qe->wnode2, true);
if (!err)
qe->second_locked = true;
else if (err != -EAGAIN)
goto err_unlock;
} else {
err = try_get_path2(f, qe->nodeid1, qe->name1, qe->nodeid2,
qe->name2, qe->path1, qe->path2, qe->wnode1,
qe->wnode2);
}
if (qe->first_locked && (qe->second_locked || !qe->path2)) {
err = 0;
goto done;
}
if (err == -EAGAIN)
return; /* keep trying */
/*
* Only let the first element be partially locked otherwise there could
* be a deadlock.
*
* But do allow the first element to be partially locked to prevent
* starvation.
*/
if (!first)
queue_element_unlock(f, qe);
/* keep trying */
return;
err_unlock:
queue_element_unlock(f, qe);
done:
qe->err = err;
qe->done = true;
pthread_cond_signal(&qe->cond);
@@ -1180,8 +1154,6 @@ static void queue_path(struct fuse *f, struct lock_queue_element *qe)
struct lock_queue_element **qp;
qe->done = false;
qe->first_locked = false;
qe->second_locked = false;
pthread_cond_init(&qe->cond, NULL);
qe->next = NULL;
for (qp = &f->lockq; *qp != NULL; qp = &(*qp)->next);
@@ -1246,7 +1218,7 @@ static int get_path_nullok(struct fuse *f, fuse_ino_t nodeid, char **path)
*path = NULL;
} else {
err = get_path_common(f, nodeid, NULL, path, NULL);
if (err == -ENOENT)
if (err == -ESTALE)
err = 0;
}
@@ -1308,27 +1280,6 @@ static int check_dir_loop(struct fuse *f,
}
#endif
static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
fuse_ino_t nodeid2, const char *name2,
char **path1, char **path2,
struct node **wnode1, struct node **wnode2)
{
int err;
/* FIXME: locking two paths needs deadlock checking */
err = try_get_path(f, nodeid1, name1, path1, wnode1, true);
if (!err) {
err = try_get_path(f, nodeid2, name2, path2, wnode2, true);
if (err) {
struct node *wn1 = wnode1 ? *wnode1 : NULL;
unlock_path(f, nodeid1, wn1, NULL);
free(*path1);
}
}
return err;
}
static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
fuse_ino_t nodeid2, const char *name2,
char **path1, char **path2,
@@ -2668,9 +2619,6 @@ void fuse_fs_destroy(struct fuse_fs *fs)
fuse_get_context()->private_data = fs->user_data;
if (fs->op.destroy)
fs->op.destroy(fs->user_data);
if (fs->m)
fuse_put_module(fs->m);
free(fs);
}
static void fuse_lib_destroy(void *data)
@@ -2679,7 +2627,6 @@ static void fuse_lib_destroy(void *data)
fuse_create_context(f);
fuse_fs_destroy(f->fs);
f->fs = NULL;
}
static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
@@ -3016,6 +2963,20 @@ static void fuse_lib_unlink(fuse_req_t req, fuse_ino_t parent,
fuse_prepare_interrupt(f, req, &d);
if (!f->conf.hard_remove && is_open(f, parent, name)) {
err = hide_node(f, path, parent, name);
if (!err) {
/* we have hidden the node so now check again under a lock in case it is not used any more */
if (!is_open(f, parent, wnode->name)) {
char *unlinkpath;
/* get the hidden file path, to unlink it */
if (try_get_path(f, wnode->nodeid, NULL, &unlinkpath, NULL, false) == 0) {
err = fuse_fs_unlink(f->fs, unlinkpath);
if (!err)
remove_node(f, parent, wnode->name);
free(unlinkpath);
}
}
}
} else {
err = fuse_fs_unlink(f->fs, path);
if (!err)
@@ -3192,7 +3153,9 @@ static void fuse_lib_create(fuse_req_t req, fuse_ino_t parent,
fi->direct_io = 1;
if (f->conf.kernel_cache)
fi->keep_cache = 1;
if (fi->direct_io &&
f->conf.parallel_direct_writes)
fi->parallel_direct_writes = 1;
}
}
fuse_finish_interrupt(f, req, &d);
@@ -3272,6 +3235,14 @@ static void fuse_lib_open(fuse_req_t req, fuse_ino_t ino,
if (f->conf.auto_cache)
open_auto_cache(f, ino, path, fi);
if (f->conf.no_rofd_flush &&
(fi->flags & O_ACCMODE) == O_RDONLY)
fi->noflush = 1;
if (fi->direct_io && f->conf.parallel_direct_writes)
fi->parallel_direct_writes = 1;
}
fuse_finish_interrupt(f, req, &d);
}
@@ -3403,6 +3374,8 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
err = fuse_fs_opendir(f->fs, path, &fi);
fuse_finish_interrupt(f, req, &d);
dh->fh = fi.fh;
llfi->cache_readdir = fi.cache_readdir;
llfi->keep_cache = fi.keep_cache;
}
if (!err) {
if (fuse_reply_open(req, llfi) == -ENOENT) {
@@ -4289,6 +4262,8 @@ static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned int cmd,
fuse_finish_interrupt(f, req, &d);
free_path(f, ino, path);
if (err < 0)
goto err;
fuse_reply_ioctl(req, err, out_buf, out_bufsz);
goto out;
err:
@@ -4574,8 +4549,8 @@ int fuse_loop(struct fuse *f)
return fuse_session_loop(f->se);
}
FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@@FUSE_3.2")
int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config)
FUSE_SYMVER("fuse_loop_mt_312", "fuse_loop_mt@@FUSE_3.12")
int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config)
{
if (f == NULL)
return -1;
@@ -4584,19 +4559,45 @@ int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config)
if (res)
return -1;
res = fuse_session_loop_mt_32(fuse_get_session(f), config);
res = fuse_session_loop_mt_312(fuse_get_session(f), config);
fuse_stop_cleanup_thread(f);
return res;
}
int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1);
FUSE_SYMVER("fuse_loop_mt_32", "fuse_loop_mt@FUSE_3.2")
int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config_v1 *config_v1)
{
struct fuse_loop_config *config = fuse_loop_cfg_create();
if (config == NULL)
return ENOMEM;
fuse_loop_cfg_convert(config, config_v1);
int res = fuse_loop_mt_312(f, config);
fuse_loop_cfg_destroy(config);
return res;
}
int fuse_loop_mt_31(struct fuse *f, int clone_fd);
FUSE_SYMVER("fuse_loop_mt_31", "fuse_loop_mt@FUSE_3.0")
int fuse_loop_mt_31(struct fuse *f, int clone_fd)
{
struct fuse_loop_config config;
config.clone_fd = clone_fd;
config.max_idle_threads = 10;
return fuse_loop_mt_32(f, &config);
int err;
struct fuse_loop_config *config = fuse_loop_cfg_create();
if (config == NULL)
return ENOMEM;
fuse_loop_cfg_set_clone_fd(config, clone_fd);
err = fuse_loop_mt_312(f, config);
fuse_loop_cfg_destroy(config);
return err;
}
void fuse_exit(struct fuse *f)
@@ -4653,6 +4654,7 @@ static const struct fuse_opt fuse_lib_opts[] = {
FUSE_LIB_OPT("kernel_cache", kernel_cache, 1),
FUSE_LIB_OPT("auto_cache", auto_cache, 1),
FUSE_LIB_OPT("noauto_cache", auto_cache, 0),
FUSE_LIB_OPT("no_rofd_flush", no_rofd_flush, 1),
FUSE_LIB_OPT("umask=", set_mode, 1),
FUSE_LIB_OPT("umask=%o", umask, 0),
FUSE_LIB_OPT("uid=", set_uid, 1),
@@ -4667,6 +4669,7 @@ static const struct fuse_opt fuse_lib_opts[] = {
FUSE_LIB_OPT("noforget", remember, -1),
FUSE_LIB_OPT("remember=%u", remember, 0),
FUSE_LIB_OPT("modules=%s", modules, 0),
FUSE_LIB_OPT("parallel_direct_write=%d", parallel_direct_writes, 0),
FUSE_OPT_END
};
@@ -4705,6 +4708,7 @@ void fuse_lib_help(struct fuse_args *args)
printf(
" -o kernel_cache cache files in kernel\n"
" -o [no]auto_cache enable caching based on modification times (off)\n"
" -o no_rofd_flush disable flushing of read-only fd on close (off)\n"
" -o umask=M set file permissions (octal)\n"
" -o uid=N set file owner\n"
" -o gid=N set file group\n"
@@ -4803,7 +4807,6 @@ static int fuse_push_module(struct fuse *f, const char *module,
fuse_put_module(m);
return -1;
}
newfs->m = m;
f->fs = newfs;
return 0;
}
@@ -5014,8 +5017,6 @@ out_free_name_table:
out_free_session:
fuse_session_destroy(f->se);
out_free_fs:
if (f->fs->m)
fuse_put_module(f->fs->m);
free(f->fs);
free(f->conf.modules);
out_delete_context_key:
@@ -5100,6 +5101,7 @@ void fuse_destroy(struct fuse *f)
free(f->name_table.array);
pthread_mutex_destroy(&f->lock);
fuse_session_destroy(f->se);
free(f->fs);
free(f->conf.modules);
free(f);
fuse_delete_context_key();
+62 -2
View File
@@ -45,6 +45,7 @@ struct fuse_session {
char *mountpoint;
volatile int exited;
int fd;
struct fuse_custom_io *io;
struct mount_opts *mo;
int debug;
int deny_others;
@@ -87,6 +88,57 @@ struct fuse_module {
int ctr;
};
/**
* Configuration parameters passed to fuse_session_loop_mt() and
* fuse_loop_mt().
*
* Internal API to avoid exposing the plain data structure and
* causing compat issues after adding or removing struct members.
*
*/
#if FUSE_USE_VERSION >= FUSE_MAKE_VERSION(3, 12)
struct fuse_loop_config
{
/* verififier that a correct struct was was passed. This is especially
* needed, as versions below (3, 12) were using a public struct
* (now called fuse_loop_config_v1), which was hard to extend with
* additional parameters, without risking that file system implementations
* would not have noticed and might either pass uninitialized members
* or even too small structs.
* fuse_loop_config_v1 has clone_fd at this offset, which should be either 0
* or 1. v2 or even higher version just need to set a value here
* which not conflicting and very unlikely as having been set by
* file system implementation.
*/
int version_id;
/**
* whether to use separate device fds for each thread
* (may increase performance)
*/
int clone_fd;
/**
* The maximum number of available worker threads before they
* start to get deleted when they become idle. If not
* specified, the default is 10.
*
* Adjusting this has performance implications; a very small number
* of threads in the pool will cause a lot of thread creation and
* deletion overhead and performance may suffer. When set to 0, a new
* thread will be created to service every operation.
* The special value of -1 means that this parameter is disabled.
*/
int max_idle_threads;
/**
* max number of threads taking and processing kernel requests
*
* As of now threads are created dynamically
*/
unsigned int max_threads;
};
#endif
/* ----------------------------------------------------------- *
* Channel interface (when using -o clone_fd) *
* ----------------------------------------------------------- */
@@ -128,8 +180,16 @@ void fuse_session_process_buf_int(struct fuse_session *se,
struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op,
size_t op_size, void *private_data);
int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config);
int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config);
int fuse_loop_mt_312(struct fuse *f, struct fuse_loop_config *config);
int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
/**
* Internal verifier for the given config.
*
* @return negative standard error code or 0 on success
*/
int fuse_loop_cfg_verify(struct fuse_loop_config *config);
#define FUSE_MAX_MAX_PAGES 256
#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32
+1 -1
View File
@@ -8,7 +8,7 @@
See the file COPYING.LIB
*/
#include "config.h"
#include "fuse_config.h"
#include "fuse_lowlevel.h"
#include "fuse_i.h"
+138 -11
View File
@@ -8,7 +8,7 @@
See the file COPYING.LIB.
*/
#include "config.h"
#include "fuse_config.h"
#include "fuse_lowlevel.h"
#include "fuse_misc.h"
#include "fuse_kernel.h"
@@ -24,10 +24,20 @@
#include <sys/time.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <limits.h>
/* Environment var controlling the thread stack size */
#define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
#define FUSE_LOOP_MT_V2_IDENTIFIER INT_MAX - 2
#define FUSE_LOOP_MT_DEF_CLONE_FD 0
#define FUSE_LOOP_MT_DEF_MAX_THREADS 10
#define FUSE_LOOP_MT_DEF_IDLE_THREADS -1 /* thread destruction is disabled
* by default */
/* an arbitrary large value that cannot be valid */
#define FUSE_LOOP_MT_MAX_THREADS (100U * 1000)
struct fuse_worker {
struct fuse_worker *prev;
struct fuse_worker *next;
@@ -51,6 +61,7 @@ struct fuse_mt {
int error;
int clone_fd;
int max_idle;
int max_threads;
};
static struct fuse_chan *fuse_chan_new(int fd)
@@ -155,7 +166,7 @@ static void *fuse_do_work(void *data)
if (!isforget)
mt->numavail--;
if (mt->numavail == 0)
if (mt->numavail == 0 && mt->numworker < mt->max_threads)
fuse_loop_start_thread(mt);
pthread_mutex_unlock(&mt->lock);
@@ -164,7 +175,14 @@ static void *fuse_do_work(void *data)
pthread_mutex_lock(&mt->lock);
if (!isforget)
mt->numavail++;
if (mt->numavail > mt->max_idle) {
/* creating and destroying threads is rather expensive - and there is
* not much gain from destroying existing threads. It is therefore
* discouraged to set max_idle to anything else than -1. If there
* is indeed a good reason to destruct threads it should be done
* delayed, a moving average might be useful for that.
*/
if (mt->max_idle != -1 && mt->numavail > mt->max_idle && mt->numworker > 1) {
if (mt->exit) {
pthread_mutex_unlock(&mt->lock);
return NULL;
@@ -196,7 +214,10 @@ int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
pthread_attr_t attr;
char *stack_size;
/* Override default stack size */
/* Override default stack size
* XXX: This should ideally be a parameter option. It is rather
* well hidden here.
*/
pthread_attr_init(&attr);
stack_size = getenv(ENVNAME_THREAD_STACK);
if (stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
@@ -303,12 +324,25 @@ static void fuse_join_worker(struct fuse_mt *mt, struct fuse_worker *w)
free(w);
}
FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@@FUSE_3.2")
int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config)
int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config);
FUSE_SYMVER("fuse_session_loop_mt_312", "fuse_session_loop_mt@@FUSE_3.12")
int fuse_session_loop_mt_312(struct fuse_session *se, struct fuse_loop_config *config)
{
int err;
int err;
struct fuse_mt mt;
struct fuse_worker *w;
int created_config = 0;
if (config) {
err = fuse_loop_cfg_verify(config);
if (err)
return err;
} else {
/* The caller does not care about parameters - use the default */
config = fuse_loop_cfg_create();
created_config = 1;
}
memset(&mt, 0, sizeof(struct fuse_mt));
mt.se = se;
@@ -317,6 +351,7 @@ int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *co
mt.numworker = 0;
mt.numavail = 0;
mt.max_idle = config->max_idle_threads;
mt.max_threads = config->max_threads;
mt.main.thread_id = pthread_self();
mt.main.prev = mt.main.next = &mt.main;
sem_init(&mt.finish, 0, 0);
@@ -347,15 +382,107 @@ int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *co
if(se->error != 0)
err = se->error;
fuse_session_reset(se);
if (created_config) {
fuse_loop_cfg_destroy(config);
config = NULL;
}
return err;
}
int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1);
FUSE_SYMVER("fuse_session_loop_mt_32", "fuse_session_loop_mt@FUSE_3.2")
int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config_v1 *config_v1)
{
int err;
struct fuse_loop_config *config = NULL;
if (config_v1 != NULL) {
/* convert the given v1 config */
config = fuse_loop_cfg_create();
if (config == NULL)
return ENOMEM;
fuse_loop_cfg_convert(config, config_v1);
}
err = fuse_session_loop_mt_312(se, config);
fuse_loop_cfg_destroy(config);
return err;
}
int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd);
FUSE_SYMVER("fuse_session_loop_mt_31", "fuse_session_loop_mt@FUSE_3.0")
int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd)
{
struct fuse_loop_config config;
config.clone_fd = clone_fd;
config.max_idle_threads = 10;
return fuse_session_loop_mt_32(se, &config);
struct fuse_loop_config *config = fuse_loop_cfg_create();
if (clone_fd > 0)
fuse_loop_cfg_set_clone_fd(config, clone_fd);
return fuse_session_loop_mt_312(se, config);
}
struct fuse_loop_config *fuse_loop_cfg_create(void)
{
struct fuse_loop_config *config = calloc(1, sizeof(*config));
if (config == NULL)
return NULL;
config->version_id = FUSE_LOOP_MT_V2_IDENTIFIER;
config->max_idle_threads = FUSE_LOOP_MT_DEF_IDLE_THREADS;
config->max_threads = FUSE_LOOP_MT_DEF_MAX_THREADS;
config->clone_fd = FUSE_LOOP_MT_DEF_CLONE_FD;
return config;
}
void fuse_loop_cfg_destroy(struct fuse_loop_config *config)
{
free(config);
}
int fuse_loop_cfg_verify(struct fuse_loop_config *config)
{
if (config->version_id != FUSE_LOOP_MT_V2_IDENTIFIER)
return -EINVAL;
return 0;
}
void fuse_loop_cfg_convert(struct fuse_loop_config *config,
struct fuse_loop_config_v1 *v1_conf)
{
fuse_loop_cfg_set_idle_threads(config, v1_conf->max_idle_threads);
fuse_loop_cfg_set_clone_fd(config, v1_conf->clone_fd);
}
void fuse_loop_cfg_set_idle_threads(struct fuse_loop_config *config,
unsigned int value)
{
if (value > FUSE_LOOP_MT_MAX_THREADS) {
if (value != UINT_MAX)
fuse_log(FUSE_LOG_ERR,
"Ignoring invalid max threads value "
"%u > max (%u).\n", value,
FUSE_LOOP_MT_MAX_THREADS);
return;
}
config->max_idle_threads = value;
}
void fuse_loop_cfg_set_max_threads(struct fuse_loop_config *config,
unsigned int value)
{
config->max_threads = value;
}
void fuse_loop_cfg_set_clone_fd(struct fuse_loop_config *config,
unsigned int value)
{
config->clone_fd = value;
}
+209 -55
View File
@@ -11,7 +11,7 @@
#define _GNU_SOURCE
#include "config.h"
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_kernel.h"
#include "fuse_opt.h"
@@ -123,6 +123,7 @@ static void list_add_req(struct fuse_req *req, struct fuse_req *next)
static void destroy_req(fuse_req_t req)
{
assert(req->ch == NULL);
pthread_mutex_destroy(&req->lock);
free(req);
}
@@ -185,8 +186,15 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch,
}
}
ssize_t res = writev(ch ? ch->fd : se->fd,
iov, count);
ssize_t res;
if (se->io != NULL)
/* se->io->writev is never NULL if se->io is not NULL as
specified by fuse_session_custom_io()*/
res = se->io->writev(ch ? ch->fd : se->fd, iov, count,
se->userdata);
else
res = writev(ch ? ch->fd : se->fd, iov, count);
int err = errno;
if (res == -1) {
@@ -205,7 +213,12 @@ int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov,
{
struct fuse_out_header out;
#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
const char *str = strerrordesc_np(error * -1);
if ((str == NULL && error != 0) || error > 0) {
#else
if (error <= -1000 || error > 0) {
#endif
fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error);
error = -ERANGE;
}
@@ -395,6 +408,10 @@ static void fill_open(struct fuse_open_out *arg,
arg->open_flags |= FOPEN_CACHE_DIR;
if (f->nonseekable)
arg->open_flags |= FOPEN_NONSEEKABLE;
if (f->noflush)
arg->open_flags |= FOPEN_NOFLUSH;
if (f->parallel_direct_writes)
arg->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
}
int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
@@ -814,8 +831,14 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch,
(se->conn.want & FUSE_CAP_SPLICE_MOVE))
splice_flags |= SPLICE_F_MOVE;
res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd,
NULL, out->len, splice_flags);
if (se->io != NULL && se->io->splice_send != NULL) {
res = se->io->splice_send(llp->pipe[0], NULL,
ch ? ch->fd : se->fd, NULL, out->len,
splice_flags, se->userdata);
} else {
res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, NULL,
out->len, splice_flags);
}
if (res == -1) {
res = -errno;
perror("fuse: splice from pipe");
@@ -1170,6 +1193,8 @@ static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
FUSE_SET_ATTR_SIZE |
FUSE_SET_ATTR_ATIME |
FUSE_SET_ATTR_MTIME |
FUSE_SET_ATTR_KILL_SUID |
FUSE_SET_ATTR_KILL_SGID |
FUSE_SET_ATTR_ATIME_NOW |
FUSE_SET_ATTR_MTIME_NOW |
FUSE_SET_ATTR_CTIME;
@@ -1562,10 +1587,14 @@ static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
{
struct fuse_session *se = req->se;
unsigned int xattr_ext = !!(se->conn.want & FUSE_CAP_SETXATTR_EXT);
struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg;
char *name = PARAM(arg);
char *name = xattr_ext ? PARAM(arg) :
(char *)arg + FUSE_COMPAT_SETXATTR_IN_SIZE;
char *value = name + strlen(name) + 1;
/* XXX:The API should be extended to support extra_flags/setxattr_flags */
if (req->se->op.setxattr)
req->se->op.setxattr(req, nodeid, name, value, arg->size,
arg->flags);
@@ -1710,8 +1739,11 @@ static int find_interrupted(struct fuse_session *se, struct fuse_req *req)
pthread_mutex_lock(&se->lock);
curr->ctr--;
if (!curr->ctr)
if (!curr->ctr) {
fuse_chan_put(req->ch);
req->ch = NULL;
destroy_req(curr);
}
return 1;
}
@@ -1737,9 +1769,11 @@ static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
req->u.i.unique = arg->unique;
pthread_mutex_lock(&se->lock);
if (find_interrupted(se, req))
if (find_interrupted(se, req)) {
fuse_chan_put(req->ch);
req->ch = NULL;
destroy_req(req);
else
} else
list_add_req(req, &se->interrupts);
pthread_mutex_unlock(&se->lock);
}
@@ -1754,7 +1788,9 @@ static struct fuse_req *check_interrupt(struct fuse_session *se,
if (curr->u.i.unique == req->unique) {
req->interrupted = 1;
list_del_req(curr);
free(curr);
fuse_chan_put(curr->ch);
curr->ch = NULL;
destroy_req(curr);
return NULL;
}
}
@@ -1898,7 +1934,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
struct fuse_session *se = req->se;
size_t bufsize = se->bufsize;
size_t outargsize = sizeof(outarg);
uint64_t inargflags = 0;
uint64_t outargflags = 0;
(void) nodeid;
if (se->debug) {
fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor);
@@ -1933,43 +1970,48 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (arg->minor >= 6) {
if (arg->max_readahead < se->conn.max_readahead)
se->conn.max_readahead = arg->max_readahead;
if (arg->flags & FUSE_ASYNC_READ)
inargflags = arg->flags;
if (inargflags & FUSE_INIT_EXT)
inargflags = inargflags | (uint64_t) arg->flags2 << 32;
if (inargflags & FUSE_ASYNC_READ)
se->conn.capable |= FUSE_CAP_ASYNC_READ;
if (arg->flags & FUSE_POSIX_LOCKS)
if (inargflags & FUSE_POSIX_LOCKS)
se->conn.capable |= FUSE_CAP_POSIX_LOCKS;
if (arg->flags & FUSE_ATOMIC_O_TRUNC)
if (inargflags & FUSE_ATOMIC_O_TRUNC)
se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC;
if (arg->flags & FUSE_EXPORT_SUPPORT)
if (inargflags & FUSE_EXPORT_SUPPORT)
se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT;
if (arg->flags & FUSE_DONT_MASK)
if (inargflags & FUSE_DONT_MASK)
se->conn.capable |= FUSE_CAP_DONT_MASK;
if (arg->flags & FUSE_FLOCK_LOCKS)
if (inargflags & FUSE_FLOCK_LOCKS)
se->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
if (arg->flags & FUSE_AUTO_INVAL_DATA)
if (inargflags & FUSE_AUTO_INVAL_DATA)
se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
if (arg->flags & FUSE_DO_READDIRPLUS)
if (inargflags & FUSE_DO_READDIRPLUS)
se->conn.capable |= FUSE_CAP_READDIRPLUS;
if (arg->flags & FUSE_READDIRPLUS_AUTO)
if (inargflags & FUSE_READDIRPLUS_AUTO)
se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO;
if (arg->flags & FUSE_ASYNC_DIO)
if (inargflags & FUSE_ASYNC_DIO)
se->conn.capable |= FUSE_CAP_ASYNC_DIO;
if (arg->flags & FUSE_WRITEBACK_CACHE)
if (inargflags & FUSE_WRITEBACK_CACHE)
se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE;
if (arg->flags & FUSE_NO_OPEN_SUPPORT)
if (inargflags & FUSE_NO_OPEN_SUPPORT)
se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT;
if (arg->flags & FUSE_PARALLEL_DIROPS)
if (inargflags & FUSE_PARALLEL_DIROPS)
se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS;
if (arg->flags & FUSE_POSIX_ACL)
if (inargflags & FUSE_POSIX_ACL)
se->conn.capable |= FUSE_CAP_POSIX_ACL;
if (arg->flags & FUSE_HANDLE_KILLPRIV)
if (inargflags & FUSE_HANDLE_KILLPRIV)
se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
if (arg->flags & FUSE_CACHE_SYMLINKS)
if (inargflags & FUSE_CACHE_SYMLINKS)
se->conn.capable |= FUSE_CAP_CACHE_SYMLINKS;
if (arg->flags & FUSE_NO_OPENDIR_SUPPORT)
if (inargflags & FUSE_NO_OPENDIR_SUPPORT)
se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT;
if (arg->flags & FUSE_EXPLICIT_INVAL_DATA)
if (inargflags & FUSE_EXPLICIT_INVAL_DATA)
se->conn.capable |= FUSE_CAP_EXPLICIT_INVAL_DATA;
if (!(arg->flags & FUSE_MAX_PAGES)) {
if (inargflags & FUSE_SETXATTR_EXT)
se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
if (!(inargflags & FUSE_MAX_PAGES)) {
size_t max_bufsize =
FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize()
+ FUSE_BUFFER_HEADER_SIZE;
@@ -1977,6 +2019,8 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
bufsize = max_bufsize;
}
}
if (arg->minor >= 38)
se->conn.capable |= FUSE_CAP_EXPIRE_ONLY;
} else {
se->conn.max_readahead = 0;
}
@@ -1984,9 +2028,13 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
if (se->conn.proto_minor >= 14) {
#ifdef HAVE_SPLICE
#ifdef HAVE_VMSPLICE
se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
if ((se->io == NULL) || (se->io->splice_send != NULL)) {
se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
}
#endif
se->conn.capable |= FUSE_CAP_SPLICE_READ;
if ((se->io == NULL) || (se->io->splice_receive != NULL)) {
se->conn.capable |= FUSE_CAP_SPLICE_READ;
}
#endif
}
if (se->conn.proto_minor >= 18)
@@ -2016,6 +2064,12 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS);
LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir,
FUSE_CAP_READDIRPLUS_AUTO);
/* This could safely become default, but libfuse needs an API extension
* to support it
* LL_SET_DEFAULT(1, FUSE_CAP_SETXATTR_EXT);
*/
se->conn.time_gran = 1;
if (bufsize < FUSE_MIN_READ_BUFFER) {
@@ -2060,39 +2114,49 @@ void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
outarg.flags |= FUSE_MAX_PAGES;
outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1;
}
outargflags = outarg.flags;
/* Always enable big writes, this is superseded
by the max_write option */
outarg.flags |= FUSE_BIG_WRITES;
outargflags |= FUSE_BIG_WRITES;
if (se->conn.want & FUSE_CAP_ASYNC_READ)
outarg.flags |= FUSE_ASYNC_READ;
outargflags |= FUSE_ASYNC_READ;
if (se->conn.want & FUSE_CAP_POSIX_LOCKS)
outarg.flags |= FUSE_POSIX_LOCKS;
outargflags |= FUSE_POSIX_LOCKS;
if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC)
outarg.flags |= FUSE_ATOMIC_O_TRUNC;
outargflags |= FUSE_ATOMIC_O_TRUNC;
if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT)
outarg.flags |= FUSE_EXPORT_SUPPORT;
outargflags |= FUSE_EXPORT_SUPPORT;
if (se->conn.want & FUSE_CAP_DONT_MASK)
outarg.flags |= FUSE_DONT_MASK;
outargflags |= FUSE_DONT_MASK;
if (se->conn.want & FUSE_CAP_FLOCK_LOCKS)
outarg.flags |= FUSE_FLOCK_LOCKS;
outargflags |= FUSE_FLOCK_LOCKS;
if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA)
outarg.flags |= FUSE_AUTO_INVAL_DATA;
outargflags |= FUSE_AUTO_INVAL_DATA;
if (se->conn.want & FUSE_CAP_READDIRPLUS)
outarg.flags |= FUSE_DO_READDIRPLUS;
outargflags |= FUSE_DO_READDIRPLUS;
if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO)
outarg.flags |= FUSE_READDIRPLUS_AUTO;
outargflags |= FUSE_READDIRPLUS_AUTO;
if (se->conn.want & FUSE_CAP_ASYNC_DIO)
outarg.flags |= FUSE_ASYNC_DIO;
outargflags |= FUSE_ASYNC_DIO;
if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE)
outarg.flags |= FUSE_WRITEBACK_CACHE;
outargflags |= FUSE_WRITEBACK_CACHE;
if (se->conn.want & FUSE_CAP_POSIX_ACL)
outarg.flags |= FUSE_POSIX_ACL;
outargflags |= FUSE_POSIX_ACL;
if (se->conn.want & FUSE_CAP_CACHE_SYMLINKS)
outarg.flags |= FUSE_CACHE_SYMLINKS;
outargflags |= FUSE_CACHE_SYMLINKS;
if (se->conn.want & FUSE_CAP_EXPLICIT_INVAL_DATA)
outarg.flags |= FUSE_EXPLICIT_INVAL_DATA;
outargflags |= FUSE_EXPLICIT_INVAL_DATA;
if (se->conn.want & FUSE_CAP_SETXATTR_EXT)
outargflags |= FUSE_SETXATTR_EXT;
if (inargflags & FUSE_INIT_EXT) {
outargflags |= FUSE_INIT_EXT;
outarg.flags2 = outargflags >> 32;
}
outarg.flags = outargflags;
outarg.max_readahead = se->conn.max_readahead;
outarg.max_write = se->conn.max_write;
if (se->conn.proto_minor >= 13) {
@@ -2246,21 +2310,43 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino,
return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2);
}
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen)
/**
* Notify parent attributes and the dentry matching parent/name
*
* Underlying base function for fuse_lowlevel_notify_inval_entry() and
* fuse_lowlevel_notify_expire_entry().
*
* @warning
* Only checks if fuse_lowlevel_notify_inval_entry() is supported by
* the kernel. All other flags will fall back to
* fuse_lowlevel_notify_inval_entry() if not supported!
* DO THE PROPER CHECKS IN THE DERIVED FUNCTION!
*
* @param se the session object
* @param parent inode number
* @param name file name
* @param namelen strlen() of file name
* @param flags flags to control if the entry should be expired or invalidated
* @return zero for success, -errno for failure
*/
static int fuse_lowlevel_notify_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen,
enum fuse_notify_entry_flags flags)
{
struct fuse_notify_inval_entry_out outarg;
struct iovec iov[3];
if (!se)
return -EINVAL;
if (se->conn.proto_minor < 12)
return -ENOSYS;
outarg.parent = parent;
outarg.namelen = namelen;
outarg.padding = 0;
outarg.flags = 0;
if (flags & FUSE_LL_EXPIRE_ONLY)
outarg.flags |= FUSE_EXPIRE_ONLY;
iov[1].iov_base = &outarg;
iov[1].iov_len = sizeof(outarg);
@@ -2270,6 +2356,25 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3);
}
int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen)
{
return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_INVALIDATE);
}
int fuse_lowlevel_notify_expire_entry(struct fuse_session *se, fuse_ino_t parent,
const char *name, size_t namelen)
{
if (!se)
return -EINVAL;
if (!(se->conn.capable & FUSE_CAP_EXPIRE_ONLY))
return -ENOSYS;
return fuse_lowlevel_notify_entry(se, parent, name, namelen, FUSE_LL_EXPIRE_ONLY);
}
int fuse_lowlevel_notify_delete(struct fuse_session *se,
fuse_ino_t parent, fuse_ino_t child,
const char *name, size_t namelen)
@@ -2721,6 +2826,8 @@ void fuse_session_destroy(struct fuse_session *se)
free(se->cuse_data);
if (se->fd != -1)
close(se->fd);
if (se->io != NULL)
free(se->io);
destroy_mount_opts(se->mo);
free(se);
}
@@ -2770,8 +2877,14 @@ int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf,
goto fallback;
}
res = splice(ch ? ch->fd : se->fd,
NULL, llp->pipe[1], NULL, bufsize, 0);
if (se->io != NULL && se->io->splice_receive != NULL) {
res = se->io->splice_receive(ch ? ch->fd : se->fd, NULL,
llp->pipe[1], NULL, bufsize, 0,
se->userdata);
} else {
res = splice(ch ? ch->fd : se->fd, NULL, llp->pipe[1], NULL,
bufsize, 0);
}
err = errno;
if (fuse_session_exited(se))
@@ -2857,7 +2970,14 @@ fallback:
}
restart:
res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize);
if (se->io != NULL) {
/* se->io->read is never NULL if se->io is not NULL as
specified by fuse_session_custom_io()*/
res = se->io->read(ch ? ch->fd : se->fd, buf->mem, se->bufsize,
se->userdata);
} else {
res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize);
}
err = errno;
if (fuse_session_exited(se))
@@ -2987,6 +3107,40 @@ out1:
return NULL;
}
int fuse_session_custom_io(struct fuse_session *se, const struct fuse_custom_io *io,
int fd)
{
if (fd < 0) {
fuse_log(FUSE_LOG_ERR, "Invalid file descriptor value %d passed to "
"fuse_session_custom_io()\n", fd);
return -EBADF;
}
if (io == NULL) {
fuse_log(FUSE_LOG_ERR, "No custom IO passed to "
"fuse_session_custom_io()\n");
return -EINVAL;
} else if (io->read == NULL || io->writev == NULL) {
/* If the user provides their own file descriptor, we can't
guarantee that the default behavior of the io operations made
in libfuse will function properly. Therefore, we enforce the
user to implement these io operations when using custom io. */
fuse_log(FUSE_LOG_ERR, "io passed to fuse_session_custom_io() must "
"implement both io->read() and io->writev\n");
return -EINVAL;
}
se->io = malloc(sizeof(struct fuse_custom_io));
if (se->io == NULL) {
fuse_log(FUSE_LOG_ERR, "Failed to allocate memory for custom io. "
"Error: %s\n", strerror(errno));
return -errno;
}
se->fd = fd;
*se->io = *io;
return 0;
}
int fuse_session_mount(struct fuse_session *se, const char *mountpoint)
{
int fd;
+4 -1
View File
@@ -11,8 +11,11 @@
/*
Versioned symbols cannot be used in some cases because it
- not supported on MacOSX (in MachO binary format)
Note: "@@" denotes the default symbol, "@" is binary a compat version.
*/
#ifndef __APPLE__
#ifdef LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS
# if HAVE_SYMVER_ATTRIBUTE
# define FUSE_SYMVER(sym1, sym2) __attribute__ ((symver (sym2)))
# else
+1 -1
View File
@@ -9,7 +9,7 @@
See the file COPYING.LIB
*/
#include "config.h"
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_opt.h"
#include "fuse_misc.h"
+1 -1
View File
@@ -8,7 +8,7 @@
See the file COPYING.LIB
*/
#include "config.h"
#include "fuse_config.h"
#include "fuse_lowlevel.h"
#include "fuse_i.h"
+21 -2
View File
@@ -39,6 +39,7 @@ FUSE_3.0 {
fuse_session_new;
fuse_main_real;
fuse_mount;
fuse_session_custom_io;
fuse_session_mount;
fuse_new;
fuse_opt_insert_arg;
@@ -82,7 +83,6 @@ FUSE_3.0 {
fuse_fs_unlink;
fuse_fs_utimens;
fuse_fs_write;
fuse_register_module;
fuse_reply_iov;
fuse_version;
fuse_pkgversion;
@@ -139,6 +139,7 @@ FUSE_3.1 {
fuse_lib_help;
fuse_invalidate_path;
fuse_new_30;
fuse_new_31;
fuse_new;
} FUSE_3.0;
@@ -159,7 +160,6 @@ FUSE_3.3 {
FUSE_3.4 {
global:
fuse_fs_copy_file_range;
fuse_reply_copy_file_range;
} FUSE_3.3;
FUSE_3.7 {
@@ -168,6 +168,25 @@ FUSE_3.7 {
fuse_log;
} FUSE_3.4;
FUSE_3.12 {
global:
fuse_session_loop_mt;
fuse_session_loop_mt_312;
fuse_loop_mt;
fuse_loop_mt_32;
fuse_loop_mt_312;
fuse_loop_cfg_create;
fuse_loop_cfg_destroy;
fuse_loop_cfg_set_idle_threads;
fuse_loop_cfg_set_max_threads;
fuse_loop_cfg_set_clone_fd;
fuse_loop_cfg_convert;
fuse_parse_cmdline;
fuse_parse_cmdline_30;
fuse_parse_cmdline_312;
fuse_lowlevel_notify_expire_entry;
} FUSE_3.4;
# Local Variables:
# indent-tabs-mode: t
# End:
+46 -9
View File
@@ -10,7 +10,7 @@
See the file COPYING.LIB.
*/
#include "config.h"
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_opt.h"
@@ -50,6 +50,7 @@ static const struct fuse_opt fuse_helper_opts[] = {
#endif
FUSE_HELPER_OPT("clone_fd", clone_fd),
FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads),
FUSE_HELPER_OPT("max_threads=%u", max_threads),
FUSE_OPT_END
};
@@ -136,6 +137,8 @@ void fuse_cmdline_help(void)
" -o clone_fd use separate fuse device fd for each thread\n"
" (may improve performance)\n"
" -o max_idle_threads the maximum number of idle worker threads\n"
" allowed (default: -1)\n"
" -o max_threads the maximum number of worker threads\n"
" allowed (default: 10)\n");
}
@@ -199,12 +202,16 @@ static int add_default_subtype(const char *progname, struct fuse_args *args)
return res;
}
int fuse_parse_cmdline(struct fuse_args *args,
struct fuse_cmdline_opts *opts)
int fuse_parse_cmdline_312(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
FUSE_SYMVER("fuse_parse_cmdline_312", "fuse_parse_cmdline@@FUSE_3.12")
int fuse_parse_cmdline_312(struct fuse_args *args,
struct fuse_cmdline_opts *opts)
{
memset(opts, 0, sizeof(struct fuse_cmdline_opts));
opts->max_idle_threads = 10;
opts->max_idle_threads = UINT_MAX; /* new default in fuse version 3.12 */
opts->max_threads = 10;
if (fuse_opt_parse(args, opts, fuse_helper_opts,
fuse_helper_opt_proc) == -1)
@@ -221,6 +228,27 @@ int fuse_parse_cmdline(struct fuse_args *args,
return 0;
}
/**
* struct fuse_cmdline_opts got extended in libfuse-3.12
*/
int fuse_parse_cmdline_30(struct fuse_args *args,
struct fuse_cmdline_opts *opts);
FUSE_SYMVER("fuse_parse_cmdline_30", "fuse_parse_cmdline@FUSE_3.0")
int fuse_parse_cmdline_30(struct fuse_args *args,
struct fuse_cmdline_opts *out_opts)
{
struct fuse_cmdline_opts opts;
int rc = fuse_parse_cmdline_312(args, &opts);
if (rc == 0) {
/* copy up to the size of the old pre 3.12 struct */
memcpy(out_opts, &opts,
offsetof(struct fuse_cmdline_opts, max_idle_threads) +
sizeof(opts.max_idle_threads));
}
return rc;
}
int fuse_daemonize(int foreground)
{
@@ -283,6 +311,7 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
struct fuse *fuse;
struct fuse_cmdline_opts opts;
int res;
struct fuse_loop_config *loop_config = NULL;
if (fuse_parse_cmdline(&args, &opts) != 0)
return 1;
@@ -338,13 +367,20 @@ int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
if (opts.singlethread)
res = fuse_loop(fuse);
else {
struct fuse_loop_config loop_config;
loop_config.clone_fd = opts.clone_fd;
loop_config.max_idle_threads = opts.max_idle_threads;
res = fuse_loop_mt_32(fuse, &loop_config);
loop_config = fuse_loop_cfg_create();
if (loop_config == NULL) {
res = 7;
goto out3;
}
fuse_loop_cfg_set_clone_fd(loop_config, opts.clone_fd);
fuse_loop_cfg_set_idle_threads(loop_config, opts.max_idle_threads);
fuse_loop_cfg_set_max_threads(loop_config, opts.max_threads);
res = fuse_loop_mt(fuse, loop_config);
}
if (res)
res = 7;
res = 8;
fuse_remove_signal_handlers(se);
out3:
@@ -352,6 +388,7 @@ out3:
out2:
fuse_destroy(fuse);
out1:
fuse_loop_cfg_destroy(loop_config);
free(opts.mountpoint);
fuse_opt_free_args(&args);
return res;
+3 -3
View File
@@ -2,7 +2,7 @@ libfuse_sources = ['fuse.c', 'fuse_i.h', 'fuse_loop.c', 'fuse_loop_mt.c',
'fuse_lowlevel.c', 'fuse_misc.h', 'fuse_opt.c',
'fuse_signals.c', 'buffer.c', 'cuse_lowlevel.c',
'helper.c', 'modules/subdir.c', 'mount_util.c',
'fuse_log.c' ]
'fuse_log.c', 'compat.c' ]
if host_machine.system().startswith('linux')
libfuse_sources += [ 'mount.c' ]
@@ -11,7 +11,7 @@ else
endif
deps = [ thread_dep ]
if cfg.get('HAVE_ICONV')
if private_cfg.get('HAVE_ICONV')
libfuse_sources += [ 'modules/iconv.c' ]
libiconv = cc.find_library('iconv', required: false)
if libiconv.found()
@@ -37,7 +37,7 @@ libfuse = library('fuse3', libfuse_sources, version: meson.project_version(),
soversion: '3', include_directories: include_dirs,
dependencies: deps, install: true,
link_depends: 'fuse_versionscript',
c_args: [ '-DFUSE_USE_VERSION=35',
c_args: [ '-DFUSE_USE_VERSION=312',
'-DFUSERMOUNT_DIR="@0@"'.format(fusermount_path) ],
link_args: ['-Wl,--version-script,' + meson.current_source_dir()
+ '/fuse_versionscript' ])
+3 -2
View File
@@ -6,7 +6,7 @@
See the file COPYING.LIB
*/
#include <config.h>
#include <fuse_config.h>
#include <fuse.h>
#include <stdio.h>
@@ -77,12 +77,13 @@ static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
inc = (pathlen + 1) * 4;
newpathlen += inc;
int dp = p - newpath;
tmp = realloc(newpath, newpathlen + 1);
err = -ENOMEM;
if (!tmp)
goto err;
p = tmp + (p - newpath);
p = tmp + dp;
plen += inc;
newpath = tmp;
}
+1 -1
View File
@@ -6,7 +6,7 @@
See the file COPYING.LIB
*/
#include <config.h>
#include <fuse_config.h>
#include <fuse.h>
#include <stdio.h>
+75 -11
View File
@@ -8,7 +8,7 @@
See the file COPYING.LIB.
*/
#include "config.h"
#include "fuse_config.h"
#include "fuse_i.h"
#include "fuse_misc.h"
#include "fuse_opt.h"
@@ -25,7 +25,8 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include "fuse_mount_compat.h"
#ifdef __NetBSD__
#include <perfuse.h>
@@ -97,6 +98,7 @@ static const struct fuse_opt fuse_mount_opts[] = {
FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
FUSE_OPT_KEY("-n", KEY_MTAB_OPT),
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
@@ -109,8 +111,9 @@ static const struct fuse_opt fuse_mount_opts[] = {
FUSE_OPT_KEY("async", KEY_KERN_FLAG),
FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
FUSE_OPT_END
};
@@ -148,8 +151,10 @@ static const struct mount_flags mount_flags[] = {
{"noexec", MS_NOEXEC, 1},
{"async", MS_SYNCHRONOUS, 0},
{"sync", MS_SYNCHRONOUS, 1},
{"atime", MS_NOATIME, 0},
{"noatime", MS_NOATIME, 1},
{"nodiratime", MS_NODIRATIME, 1},
{"norelatime", MS_RELATIME, 0},
{"nostrictatime", MS_STRICTATIME, 0},
#ifndef __NetBSD__
{"dirsync", MS_DIRSYNC, 1},
#endif
@@ -304,6 +309,65 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
waitpid(pid, NULL, 0);
}
static int setup_auto_unmount(const char *mountpoint, int quiet)
{
int fds[2], pid;
int res;
if (!mountpoint) {
fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
return -1;
}
res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
if(res == -1) {
perror("fuse: socketpair() failed");
return -1;
}
pid = fork();
if(pid == -1) {
perror("fuse: fork() failed");
close(fds[0]);
close(fds[1]);
return -1;
}
if(pid == 0) {
char env[10];
const char *argv[32];
int a = 0;
if (quiet) {
int fd = open("/dev/null", O_RDONLY);
if (fd != -1) {
dup2(fd, 1);
dup2(fd, 2);
}
}
argv[a++] = FUSERMOUNT_PROG;
argv[a++] = "--auto-unmount";
argv[a++] = "--";
argv[a++] = mountpoint;
argv[a++] = NULL;
close(fds[1]);
fcntl(fds[0], F_SETFD, 0);
snprintf(env, sizeof(env), "%i", fds[0]);
setenv(FUSE_COMMFD_ENV, env, 1);
exec_fusermount(argv);
perror("fuse: failed to exec fusermount3");
_exit(1);
}
close(fds[0]);
// Now fusermount3 will only exit when fds[1] closes automatically when our
// process exits.
return 0;
}
static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
const char *opts, int quiet)
{
@@ -404,12 +468,6 @@ static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
return -1;
}
if (mo->auto_unmount) {
/* Tell the caller to fallback to fusermount3 because
auto-unmount does not work otherwise. */
return -2;
}
fd = open(devname, O_RDWR | O_CLOEXEC);
if (fd == -1) {
if (errno == ENODEV || errno == ENOENT)
@@ -572,7 +630,13 @@ int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
goto out;
res = fuse_mount_sys(mountpoint, mo, mnt_opts);
if (res == -2) {
if (res >= 0 && mo->auto_unmount) {
if(0 > setup_auto_unmount(mountpoint, 0)) {
// Something went wrong, let's umount like in fuse_mount_sys.
umount2(mountpoint, MNT_DETACH); /* lazy umount */
res = -1;
}
} else if (res == -2) {
if (mo->fusermount_opts &&
fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
goto out;
+1 -1
View File
@@ -14,7 +14,7 @@
#include "fuse_opt.h"
#include <sys/param.h>
#include <sys/mount.h>
#include "fuse_mount_compat.h"
#include <sys/stat.h>
#include <sys/wait.h>
+5 -2
View File
@@ -8,8 +8,9 @@
See the file COPYING.LIB.
*/
#include "config.h"
#include "fuse_config.h"
#include "mount_util.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@@ -27,7 +28,9 @@
#endif
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include "fuse_mount_compat.h"
#include <sys/param.h>
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+118 -36
View File
@@ -1,5 +1,5 @@
project('libfuse3', ['c'], version: '3.10.5',
meson_version: '>= 0.42',
project('libfuse3', ['c'], version: '3.16.1',
meson_version: '>= 0.51',
default_options: [
'buildtype=debugoptimized',
'cpp_std=c++11',
@@ -16,12 +16,23 @@ elif platform == 'cygwin' or platform == 'windows'
'Take a look at http://www.secfs.net/winfsp/ instead')
endif
#
# Feature detection
#
cfg = configuration_data()
cc = meson.get_compiler('c')
#
# Feature detection, only available at libfuse compilation time,
# but not for application linking to libfuse.
#
private_cfg = configuration_data()
#
# Feature detection, the resulting config file is installed
# with the package.
# Note: Symbols need to be care fully named, to avoid conflicts
# with applications linking to libfuse and including
# this config.
#
public_cfg = configuration_data()
# Default includes when checking for presence of functions and
# struct members
include_default = '''
@@ -35,42 +46,38 @@ include_default = '''
'''
args_default = [ '-D_GNU_SOURCE' ]
cfg.set_quoted('PACKAGE_VERSION', meson.project_version())
private_cfg.set_quoted('PACKAGE_VERSION', meson.project_version())
# Test for presence of some functions
test_funcs = [ 'fork', 'fstatat', 'openat', 'readlinkat', 'pipe2',
'splice', 'vmsplice', 'posix_fallocate', 'fdatasync',
'utimensat', 'copy_file_range', 'fallocate' ]
foreach func : test_funcs
cfg.set('HAVE_' + func.to_upper(),
private_cfg.set('HAVE_' + func.to_upper(),
cc.has_function(func, prefix: include_default, args: args_default))
endforeach
cfg.set('HAVE_SETXATTR',
private_cfg.set('HAVE_SETXATTR',
cc.has_function('setxattr', prefix: '#include <sys/xattr.h>'))
cfg.set('HAVE_ICONV',
private_cfg.set('HAVE_ICONV',
cc.has_function('iconv', prefix: '#include <iconv.h>'))
# Test if structs have specific member
cfg.set('HAVE_STRUCT_STAT_ST_ATIM',
private_cfg.set('HAVE_STRUCT_STAT_ST_ATIM',
cc.has_member('struct stat', 'st_atim',
prefix: include_default,
args: args_default))
cfg.set('HAVE_STRUCT_STAT_ST_ATIMESPEC',
private_cfg.set('HAVE_STRUCT_STAT_ST_ATIMESPEC',
cc.has_member('struct stat', 'st_atimespec',
prefix: include_default,
args: args_default))
# Write the test results into config.h (stored in build directory)
configure_file(output: 'config.h',
configuration : cfg)
#
# Compiler configuration
#
add_project_arguments('-D_REENTRANT', '-DHAVE_CONFIG_H', '-Wno-sign-compare',
add_project_arguments('-D_REENTRANT', '-DHAVE_LIBFUSE_PRIVATE_CONFIG_H', '-Wno-sign-compare', '-D_FILE_OFFSET_BITS=64',
'-Wstrict-prototypes', '-Wmissing-declarations', '-Wwrite-strings',
'-fno-strict-aliasing', language: 'c')
add_project_arguments('-D_REENTRANT', '-DHAVE_CONFIG_H', '-D_GNU_SOURCE',
add_project_arguments('-D_REENTRANT', '-DHAVE_LIBFUSE_PRIVATE_CONFIG_H', '-D_GNU_SOURCE', '-D_FILE_OFFSET_BITS=64',
'-Wno-sign-compare', '-Wmissing-declarations',
'-Wwrite-strings', '-fno-strict-aliasing', language: 'cpp')
@@ -90,30 +97,104 @@ if not cc.compiles(code, args: [ '-O0', '-Werror=unused-result' ])
add_project_arguments('-Wno-unused-result', language: 'c')
endif
# gcc-10 and newer support the symver attribute which we need to use if we
# want to support LTO
# recent clang and gcc both support __has_attribute (and if they are too old
# to have __has_attribute, then they are too old to support symver)
# other compilers might not have __has_attribute, but in those cases
# it is safe for this check to fail and for us to fallback to the old _asm_
# method for symver. Anyway the attributes not supported by __has_attribute()
# unfortunately return true giving a false positive. So let's try to build
# using __attribute__ ((symver )) and see the result.
code = '''
__attribute__ ((symver ("test@TEST")))
void foo(void) {
}
# It is hard to detect if the libc supports versioned symbols. Only gnu-libc
# seems to provide that, but then glibc is the main target for libfuse, so
# enable it by default
versioned_symbols = 1
# This is an attempt to detect if another libc is used.
code = '''
int main(void) {
#if (defined(__UCLIBC__) || defined(__APPLE__))
#error /* libc does not have versioned symbols */
#endif
return 0;
}'''
if cc.compiles(code, args: [ '-O0', '-c', '-Werror'])
message('Compiler supports symver attribute')
add_project_arguments('-DHAVE_SYMVER_ATTRIBUTE', language: 'c')
else
message('Compiler does not support symver attribute')
if not cc.compiles(code, args: [ '-O0' ])
versioned_symbols = 0
endif
# The detection can be overridden, which is useful for other (above unhandled)
# libcs and also especially useful for testing
if get_option('disable-libc-symbol-version')
versioned_symbols = 0
endif
if versioned_symbols == 1
message('Enabling versioned libc symbols')
public_cfg.set('LIBFUSE_BUILT_WITH_VERSIONED_SYMBOLS', 1)
# gcc-10 and newer support the symver attribute which we need to use if we
# want to support LTO
# recent clang and gcc both support __has_attribute (and if they are too old
# to have __has_attribute, then they are too old to support symver)
# other compilers might not have __has_attribute, but in those cases
# it is safe for this check to fail and for us to fallback to the old _asm_
# method for symver. Anyway the attributes not supported by __has_attribute()
# unfortunately return true giving a false positive. So let's try to build
# using __attribute__ ((symver )) and see the result.
code = '''
__attribute__ ((symver ("test@TEST")))
void foo(void) {
}
int main(void) {
return 0;
}'''
if cc.compiles(code, args: [ '-O0', '-c', '-Werror'])
message('Compiler supports symver attribute')
add_project_arguments('-DHAVE_SYMVER_ATTRIBUTE', language: 'c')
else
message('Compiler does not support symver attribute')
endif
else
message('Disabling versioned libc symbols')
endif
# Older versions of musl libc don't unescape entries in /etc/mtab
# Try to detect this behaviour, and work around, if necessary.
detect_getmntent_needs_unescape = '''
#define _GNU_SOURCE
#include <mntent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define dir_space_tab "dir\\040space\\011tab"
int main()
{
const char *fake_mtab = "name " dir_space_tab " type opts 0 0\n";
FILE *f = fmemopen((void *)fake_mtab, strlen(fake_mtab) + 1, "r");
struct mntent *entp = getmntent(f);
fclose(f);
if(NULL == entp)
exit(EXIT_FAILURE);
if (0 == strcmp(entp->mnt_dir, dir_space_tab))
printf("needs escaping\n");
else
printf("no need to escape\n");
}
'''
if not meson.is_cross_build()
result = cc.run(detect_getmntent_needs_unescape)
if result.compiled() and result.returncode() == 0 and result.stdout().strip() == 'needs escaping'
message('getmntent does not unescape')
add_project_arguments('-DGETMNTENT_NEEDS_UNESCAPING', language: 'c')
endif
endif
# Write private test results into fuse_config.h (stored in build directory)
configure_file(output: 'fuse_config.h', configuration : private_cfg)
# Write the test results, installed with the package,
# symbols need to be properly prefixed to avoid
# symbol (define) conflicts
configure_file(output: 'libfuse_config.h',
configuration : public_cfg,
install: true, install_dir: join_paths(get_option('includedir'), 'fuse3'))
# '.' will refer to current build directory, which contains config.h
include_dirs = include_directories('include', 'lib', '.')
@@ -139,3 +220,4 @@ endif
foreach n : subdirs
subdir(n)
endforeach
+6
View File
@@ -4,6 +4,9 @@ option('disable-mtab', type : 'boolean', value : false,
option('udevrulesdir', type : 'string', value : '',
description: 'Where to install udev rules (if empty, query pkg-config(1))')
option('initscriptdir', type : 'string', value : '/etc/init.d/',
description: 'Init script installation location (if empty, disable init script installation)')
option('utils', type : 'boolean', value : true,
description: 'Whether or not to build and install helper programs')
@@ -16,3 +19,6 @@ option('useroot', type : 'boolean', value : true,
option('tests', type : 'boolean', value : true,
description: 'Compile the test files')
option('disable-libc-symbol-version', type : 'boolean', value : false,
description: 'Disable versioned symbols through libc')
+2
View File
@@ -0,0 +1,2 @@
untrusted comment: signify public key
RWRsNRJIAGSHE0C6WuVmusQlvudAPlmOOkCw1LbuOLuu+25DuAmPCpDf
+2
View File
@@ -0,0 +1,2 @@
untrusted comment: signify public key
RWQtnc3WSoYwHGAdfvtTTVX8RsAXrNwMb8xqVwlY8lYY2Fxn2QUDiPYK
+36 -11
View File
@@ -24,11 +24,14 @@ export ASAN_OPTIONS="detect_leaks=1"
export CC
# Standard build
for CC in gcc gcc-7 gcc-10 clang; do
for CC in gcc gcc-9 gcc-10 clang; do
echo "=== Building with ${CC} ==="
mkdir build-${CC}; cd build-${CC}
if [ "${CC}" == "clang" ]; then
export CXX="clang++"
export TEST_WITH_VALGRIND=false
else
export TEST_WITH_VALGRIND=true
fi
if [ ${CC} == 'gcc-7' ]; then
build_opts='-D b_lundef=false'
@@ -40,25 +43,30 @@ for CC in gcc gcc-7 gcc-10 clang; do
else
build_opts=''
fi
meson -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false)
meson setup -D werror=true ${build_opts} "${SOURCE_DIR}" || (cat meson-logs/meson-log.txt; false)
ninja
sudo chown root:root util/fusermount3
sudo chmod 4755 util/fusermount3
TEST_WITH_VALGRIND=true ${TEST_CMD}
${TEST_CMD}
cd ..
done
(cd build-$CC; sudo ninja install)
# Sanitized build
CC=clang
CXX=clang++
for san in undefined address; do
sanitized_build()
{
san=$1
additonal_option=$2
echo "=== Building with clang and ${san} sanitizer ==="
mkdir build-${san}; cd build-${san}
[ -n ${additonal_option} ] || echo "Additional option: ${additonal_option}"
mkdir build-${san}; pushd build-${san}
# b_lundef=false is required to work around clang
# bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4
meson -D b_sanitize=${san} -D b_lundef=false -D werror=true "${SOURCE_DIR}" \
meson setup -D b_sanitize=${san} -D b_lundef=false -D werror=true\
${additonal_option} "${SOURCE_DIR}" \
|| (cat meson-logs/meson-log.txt; false)
ninja
@@ -66,11 +74,28 @@ for san in undefined address; do
sudo ${TEST_CMD}
sudo chown root:root util/fusermount3
sudo chmod 4755 util/fusermount3
# Cleanup temporary files (since they're now owned by root)
# Cleanup temporary files (since they are now owned by root)
sudo rm -rf test/.pytest_cache/ test/__pycache__
${TEST_CMD}
cd ..
popd
rm -fr build-${san}
}
# Sanitized build
CC=clang
CXX=clang++
TEST_WITH_VALGRIND=false
for san in undefined address; do
sanitized_build ${san}
done
# Sanitized build without libc versioned symbols
CC=clang
CXX=clang++
for san in undefined address; do
sanitized_build ${san} "-Ddisable-libc-symbol-version=true"
done
# Documentation.
+1 -2
View File
@@ -3,8 +3,7 @@
set -e
sudo python3 -m pip install --upgrade pip
# Meson 0.45 requires Python 3.5 or newer
sudo python3 -m pip install pytest meson==0.44
sudo python3 -m pip install pytest meson==1.0.1 looseversion
valgrind --version
ninja --version
meson --version
+1 -1
View File
@@ -14,7 +14,7 @@ import threading
# example, if a request handler raises an exception, the server first signals an
# error to FUSE (causing the test to fail), and then logs the exception. Without
# the extra delay, the exception will go into nowhere.
@pytest.mark.hookwrapper
@pytest.hookimpl(hookwrapper=True)
def pytest_pyfunc_call(pyfuncitem):
outcome = yield
failed = outcome.excinfo is not None
-10
View File
@@ -1,11 +1 @@
# Suppression file for address sanitizer.
# There are some leaks in command line option parsing. They should be
# fixed at some point, but are harmless since the consume just a small,
# constant amount of memory and do not grow.
leak:fuse_opt_parse
# Leaks in fusermount3 are harmless as well (it's a short-lived
# process) - but patches are welcome!
leak:fusermount.c
+4 -1
View File
@@ -13,9 +13,12 @@ td += executable('test_syscalls', 'test_syscalls.c',
td += executable('readdir_inode', 'readdir_inode.c',
include_directories: include_dirs,
install: false)
td += executable('release_unlink_race', 'release_unlink_race.c',
dependencies: [ libfuse_dep ],
install: false)
test_scripts = [ 'conftest.py', 'pytest.ini', 'test_examples.py',
'util.py', 'test_ctests.py' ]
'util.py', 'test_ctests.py', 'test_custom_io.py' ]
td += custom_target('test_scripts', input: test_scripts,
output: test_scripts, build_by_default: true,
command: ['cp', '-fPp',
+111
View File
@@ -0,0 +1,111 @@
/*
This program can be distributed under the terms of the GNU GPLv2.
See the file COPYING.
*/
#define FUSE_USE_VERSION 31
#define _GNU_SOURCE
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
static void *xmp_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
(void) conn;
cfg->use_ino = 1;
cfg->nullpath_ok = 1;
cfg->entry_timeout = 0;
cfg->attr_timeout = 0;
cfg->negative_timeout = 0;
return NULL;
}
static int xmp_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
int res;
(void) path;
if(fi)
res = fstat(fi->fh, stbuf);
else
res = lstat(path, stbuf);
if (res == -1)
return -errno;
return 0;
}
static int xmp_unlink(const char *path)
{
int res;
res = unlink(path);
if (res == -1)
return -errno;
return 0;
}
static int xmp_rename(const char *from, const char *to, unsigned int flags)
{
int res;
if (flags)
return -EINVAL;
if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
res = rename(from, to);
if (res == -1)
return -errno;
return 0;
}
static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
int fd;
fd = open(path, fi->flags, mode);
if (fd == -1)
return -errno;
fi->fh = fd;
return 0;
}
static int xmp_release(const char *path, struct fuse_file_info *fi)
{
(void) path;
if(!getenv("RELEASEUNLINKRACE_DELAY_DISABLE")) usleep(100000);
close(fi->fh);
return 0;
}
static const struct fuse_operations xmp_oper = {
.init = xmp_init,
.getattr = xmp_getattr,
.unlink = xmp_unlink,
.rename = xmp_rename,
.create = xmp_create,
.release = xmp_release,
};
int main(int argc, char *argv[])
{
umask(0);
return fuse_main(argc, argv, &xmp_oper, NULL);
}
+6 -1
View File
@@ -9,7 +9,7 @@ import subprocess
import pytest
import platform
import sys
from distutils.version import LooseVersion
from looseversion import LooseVersion
from util import (wait_for_mount, umount, cleanup, base_cmdline,
safe_sleep, basename, fuse_test_marker, fuse_caps,
fuse_proto)
@@ -33,6 +33,11 @@ def test_write_cache(tmpdir, writeback, output_checker):
mnt_dir ]
if writeback:
cmdline.append('-owriteback_cache')
elif LooseVersion(platform.release()) >= '5.16':
# Test that close(rofd) does not block waiting for pending writes.
# This test requires kernel commit a390ccb316be ("fuse: add FOPEN_NOFLUSH")
# so opt-in for this test from kernel 5.16.
cmdline.append('--delay_ms=200')
subprocess.check_call(cmdline, stdout=output_checker.fd, stderr=output_checker.fd)
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
if __name__ == '__main__':
import sys
import pytest
sys.exit(pytest.main([__file__] + sys.argv[1:]))
import os
import socket
import struct
import subprocess
import sys
import time
from os.path import join as pjoin
import pytest
from util import base_cmdline, basename
FUSE_OP_INIT = 26
FUSE_MAJOR_VERSION = 7
FUSE_MINOR_VERSION = 38
fuse_in_header_fmt = '<IIQQIIII'
fuse_out_header_fmt = '<IiQ'
fuse_init_in_fmt = '<IIIII44x'
fuse_init_out_fmt = '<IIIIHHIIHHI28x'
def sock_recvall(sock: socket.socket, bufsize: int) -> bytes:
buf = bytes()
while len(buf) < bufsize:
buf += sock.recv(bufsize - len(buf))
return buf
def tst_init(sock: socket.socket):
unique_req = 10
dummy_init_req_header = struct.pack(
fuse_in_header_fmt, struct.calcsize(fuse_in_header_fmt) +
struct.calcsize(fuse_init_in_fmt), FUSE_OP_INIT, unique_req, 0, 0, 0,
0, 0)
dummy_init_req_payload = struct.pack(
fuse_init_in_fmt, FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION, 0, 0, 0)
dummy_init_req = dummy_init_req_header + dummy_init_req_payload
sock.sendall(dummy_init_req)
response_header = sock_recvall(sock, struct.calcsize(fuse_out_header_fmt))
packet_len, _, unique_res = struct.unpack(
fuse_out_header_fmt, response_header)
assert unique_res == unique_req
response_payload = sock_recvall(sock, packet_len - len(response_header))
response_payload = struct.unpack(fuse_init_out_fmt, response_payload)
assert response_payload[0] == FUSE_MAJOR_VERSION
def test_hello_uds(output_checker):
cmdline = base_cmdline + [pjoin(basename, 'example', 'hello_ll_uds')]
print(cmdline)
uds_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
stderr=output_checker.fd)
time.sleep(1)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.settimeout(1)
sock.connect("/tmp/libfuse-hello-ll.sock")
tst_init(sock)
sock.close()
uds_process.terminate()
try:
uds_process.wait(1)
except subprocess.TimeoutExpired:
uds_process.kill()
os.remove("/tmp/libfuse-hello-ll.sock")
+110 -5
View File
@@ -18,7 +18,7 @@ import time
import errno
import sys
import platform
from distutils.version import LooseVersion
from looseversion import LooseVersion
from tempfile import NamedTemporaryFile
from contextlib import contextmanager
from util import (wait_for_mount, umount, cleanup, base_cmdline,
@@ -104,6 +104,8 @@ def test_hello(tmpdir, name, options, cmdline_builder, output_checker):
with pytest.raises(IOError) as exc_info:
open(filename + 'does-not-exist', 'r+')
assert exc_info.value.errno == errno.ENOENT
if name == 'hello_ll':
tst_xattr(mnt_dir)
except:
cleanup(mount_process, mnt_dir)
raise
@@ -197,6 +199,8 @@ def test_passthrough_hp(short_tmpdir, cache, output_checker):
[ pjoin(basename, 'example', 'passthrough_hp'),
src_dir, mnt_dir ]
cmdline.append('--foreground')
if not cache:
cmdline.append('--nocache')
@@ -331,8 +335,9 @@ def test_null(tmpdir, output_checker):
@pytest.mark.skipif(fuse_proto < (7,12),
reason='not supported by running kernel')
@pytest.mark.parametrize("only_expire", ("invalidate_entries", "expire_entries"))
@pytest.mark.parametrize("notify", (True, False))
def test_notify_inval_entry(tmpdir, notify, output_checker):
def test_notify_inval_entry(tmpdir, only_expire, notify, output_checker):
mnt_dir = str(tmpdir)
cmdline = base_cmdline + \
[ pjoin(basename, 'example', 'notify_inval_entry'),
@@ -340,6 +345,10 @@ def test_notify_inval_entry(tmpdir, notify, output_checker):
'--timeout=5', mnt_dir ]
if not notify:
cmdline.append('--no-notify')
if only_expire == "expire_entries":
cmdline.append('--only-expire')
if fuse_proto < (7,38):
pytest.skip('only-expire not supported by running kernel')
mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
stderr=output_checker.fd)
try:
@@ -365,6 +374,37 @@ def test_notify_inval_entry(tmpdir, notify, output_checker):
else:
umount(mount_process, mnt_dir)
@pytest.mark.parametrize("intended_user", ('root', 'non_root'))
def test_dev_auto_unmount(short_tmpdir, output_checker, intended_user):
"""Check that root can mount with dev and auto_unmount
(but non-root cannot).
Split into root vs non-root, so that the output of pytest
makes clear what functionality is being tested."""
if os.getuid() == 0 and intended_user == 'non_root':
pytest.skip('needs to run as non-root')
if os.getuid() != 0 and intended_user == 'root':
pytest.skip('needs to run as root')
mnt_dir = str(short_tmpdir.mkdir('mnt'))
src_dir = str('/dev')
cmdline = base_cmdline + \
[ pjoin(basename, 'example', 'passthrough_ll'),
'-o', f'source={src_dir},dev,auto_unmount',
'-f', mnt_dir ]
mount_process = subprocess.Popen(cmdline, stdout=output_checker.fd,
stderr=output_checker.fd)
try:
wait_for_mount(mount_process, mnt_dir)
if os.getuid() == 0:
open(pjoin(mnt_dir, 'null')).close()
else:
with pytest.raises(PermissionError):
open(pjoin(mnt_dir, 'null')).close()
except:
cleanup(mount_process, mnt_dir)
raise
else:
umount(mount_process, mnt_dir)
@pytest.mark.skipif(os.getuid() != 0,
reason='needs to run as root')
def test_cuse(output_checker):
@@ -404,6 +444,65 @@ def test_cuse(output_checker):
finally:
mount_process.terminate()
def test_release_unlink_race(tmpdir, output_checker):
"""test case for Issue #746
If RELEASE and UNLINK opcodes are sent back to back, and fuse_fs_release()
and fuse_fs_rename() are slow to execute, UNLINK will run while RELEASE is
still executing. UNLINK will try to rename the file and, while the rename
is happening, the RELEASE will finish executing. As a result, RELEASE will
not detect in time that UNLINK has happened, and UNLINK will not detect in
time that RELEASE has happened.
NOTE: This is triggered only when nullpath_ok is set.
If it is NOT SET then get_path_nullok() called by fuse_lib_release() will
call get_path_common() and lock the path, and then the fuse_lib_unlink()
will wait for the path to be unlocked before executing and thus synchronise
with fuse_lib_release().
If it is SET then get_path_nullok() will just set the path to null and
return without locking anything and thus allowing fuse_lib_unlink() to
eventually execute unimpeded while fuse_lib_release() is still running.
"""
fuse_mountpoint = str(tmpdir)
fuse_binary_command = base_cmdline + \
[ pjoin(basename, 'test', 'release_unlink_race'),
"-f", fuse_mountpoint]
fuse_process = subprocess.Popen(fuse_binary_command,
stdout=output_checker.fd,
stderr=output_checker.fd)
try:
wait_for_mount(fuse_process, fuse_mountpoint)
temp_dir = tempfile.TemporaryDirectory(dir="/tmp/")
temp_dir_path = temp_dir.name
fuse_temp_file, fuse_temp_file_path = tempfile.mkstemp(dir=(fuse_mountpoint + temp_dir_path))
os.close(fuse_temp_file)
os.unlink(fuse_temp_file_path)
# needed for slow CI/CD pipelines for unlink OP to complete processing
safe_sleep(3)
assert os.listdir(temp_dir_path) == []
except:
temp_dir.cleanup()
cleanup(fuse_process, fuse_mountpoint)
raise
else:
temp_dir.cleanup()
umount(fuse_process, fuse_mountpoint)
@contextmanager
def os_open(name, flags):
fd = os.open(name, flags)
@@ -755,7 +854,13 @@ def tst_passthrough(src_dir, mnt_dir):
assert name in os.listdir(mnt_dir)
assert os.stat(src_name) == os.stat(mnt_name)
# avoid warning about unused import
test_printcap
def tst_xattr(mnt_dir):
path = os.path.join(mnt_dir, 'hello')
os.setxattr(path, b'hello_ll_setxattr_name', b'hello_ll_setxattr_value')
assert os.getxattr(path, b'hello_ll_getxattr_name') == b'hello_ll_getxattr_value'
os.removexattr(path, b'hello_ll_removexattr_name')
# avoid warning about unused import
assert test_printcap
+1 -1
View File
@@ -9,7 +9,7 @@
#define FUSE_USE_VERSION 30
#include <config.h>
#include <fuse_config.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
+7 -4
View File
@@ -1,5 +1,5 @@
#define _GNU_SOURCE
#include "config.h"
#include "fuse_config.h"
#include <stdio.h>
#include <stdlib.h>
@@ -277,7 +277,8 @@ static int fcheck_stat(int fd, int flags, struct stat *st)
if (flags & O_PATH) {
// With O_PATH fd, the server does not have to keep
// the inode alive so FUSE inode may be stale or bad
if (errno == ESTALE || errno == EIO || errno == ENOENT)
if (errno == ESTALE || errno == EIO ||
errno == ENOENT || errno == EBADF)
return 0;
}
PERROR("fstat");
@@ -1867,9 +1868,10 @@ static int test_socket(void)
int fd;
int res;
int err = 0;
const size_t test_sock_len = strlen(testsock) + 1;
start_test("socket");
if (strlen(testsock) + 1 > sizeof(su.sun_path)) {
if (test_sock_len > sizeof(su.sun_path)) {
fprintf(stderr, "Need to shorten mount point by %zu chars\n",
strlen(testsock) + 1 - sizeof(su.sun_path));
return -1;
@@ -1881,7 +1883,8 @@ static int test_socket(void)
return -1;
}
su.sun_family = AF_UNIX;
strncpy(su.sun_path, testsock, sizeof(su.sun_path) - 1);
strncpy(su.sun_path, testsock, test_sock_len);
su.sun_path[sizeof(su.sun_path) - 1] = '\0';
res = bind(fd, (struct sockaddr*)&su, sizeof(su));
if (res == -1) {
+63 -3
View File
@@ -9,7 +9,7 @@
#define FUSE_USE_VERSION 30
#include <config.h>
#include <fuse_config.h>
#include <fuse_lowlevel.h>
#include <stdio.h>
#include <stdlib.h>
@@ -19,6 +19,7 @@
#include <assert.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/stat.h>
#include <pthread.h>
#ifndef __linux__
@@ -34,9 +35,11 @@
struct options {
int writeback;
int data_size;
int delay_ms;
} options = {
.writeback = 0,
.data_size = 4096,
.data_size = 2048,
.delay_ms = 0,
};
#define OPTION(t, p) \
@@ -44,10 +47,15 @@ struct options {
static const struct fuse_opt option_spec[] = {
OPTION("writeback_cache", writeback),
OPTION("--data-size=%d", data_size),
OPTION("--delay_ms=%d", delay_ms),
FUSE_OPT_END
};
static int got_write;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int write_start, write_done;
static void tfs_init (void *userdata, struct fuse_conn_info *conn)
{
(void) userdata;
@@ -117,6 +125,9 @@ static void tfs_open(fuse_req_t req, fuse_ino_t ino,
fuse_reply_err(req, EISDIR);
else {
assert(ino == FILE_INO);
/* Test close(rofd) does not block waiting for pending writes */
fi->noflush = !options.writeback && options.delay_ms &&
(fi->flags & O_ACCMODE) == O_RDONLY;
fuse_reply_open(req, fi);
}
}
@@ -136,6 +147,22 @@ static void tfs_write(fuse_req_t req, fuse_ino_t ino, const char *buf,
expected, size);
else
got_write = 1;
/* Simulate waiting for pending writes */
if (options.delay_ms) {
pthread_mutex_lock(&lock);
write_start = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
usleep(options.delay_ms * 1000);
pthread_mutex_lock(&lock);
write_done = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
fuse_reply_write(req, size);
}
@@ -147,6 +174,25 @@ static struct fuse_lowlevel_ops tfs_oper = {
.write = tfs_write,
};
static void* close_rofd(void *data) {
int rofd = (int)(long) data;
/* Wait for first write to start */
pthread_mutex_lock(&lock);
while (!write_start && !write_done)
pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);
close(rofd);
printf("rofd closed. write_start: %d write_done: %d\n", write_start, write_done);
/* First write should not have been completed */
if (write_done)
fprintf(stderr, "ERROR: close(rofd) blocked on write!\n");
return NULL;
}
static void* run_fs(void *data) {
struct fuse_session *se = (struct fuse_session*) data;
assert(fuse_session_loop(se) == 0);
@@ -157,7 +203,8 @@ static void test_fs(char *mountpoint) {
char fname[PATH_MAX];
char *buf;
size_t dsize = options.data_size;
int fd;
int fd, rofd;
pthread_t rofd_thread;
buf = malloc(dsize);
assert(buf != NULL);
@@ -173,10 +220,23 @@ static void test_fs(char *mountpoint) {
assert(0);
}
if (options.delay_ms) {
/* Verify that close(rofd) does not block waiting for pending writes */
rofd = open(fname, O_RDONLY);
assert(pthread_create(&rofd_thread, NULL, close_rofd, (void *)(long)rofd) == 0);
/* Give close_rofd time to start */
usleep(options.delay_ms * 1000);
}
assert(write(fd, buf, dsize) == dsize);
assert(write(fd, buf, dsize) == dsize);
free(buf);
close(fd);
if (options.delay_ms) {
printf("rwfd closed. write_start: %d write_done: %d\n", write_start, write_done);
assert(pthread_join(rofd_thread, NULL) == 0);
}
}
int main(int argc, char *argv[]) {
+4 -2
View File
@@ -11,7 +11,7 @@ import itertools
basename = pjoin(os.path.dirname(__file__), '..')
def test_printcap():
def get_printcap():
cmdline = base_cmdline + [ pjoin(basename, 'example', 'printcap') ]
proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
universal_newlines=True)
@@ -31,6 +31,8 @@ def test_printcap():
return (proto, caps)
def test_printcap():
get_printcap()
def wait_for_mount(mount_process, mnt_dir,
test_fn=os.path.ismount):
@@ -163,7 +165,7 @@ os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'util'), os.environ['PATH'])
os.environ['PATH'] = '%s:%s' % (pjoin(basename, 'example'), os.environ['PATH'])
try:
(fuse_proto, fuse_caps) = test_printcap()
(fuse_proto, fuse_caps) = get_printcap()
except:
# Rely on test to raise error
fuse_proto = (0,0)
+8 -8
View File
@@ -1,17 +1,17 @@
# The file /etc/fuse.conf allows for the following parameters:
#
# user_allow_other - Using the allow_other mount option works fine as root, in
# order to have it work as user you need user_allow_other in /etc/fuse.conf as
# well. (This option allows users to use the allow_other option.) You need
# allow_other if you want users other than the owner to access a mounted fuse.
# This option must appear on a line by itself. There is no value, just the
# presence of the option.
# user_allow_other - Using the allow_other mount option works fine as root, but
# in order to have it work as a regular user, you need to set user_allow_other
# in /etc/fuse.conf as well. This option allows non-root users to use the
# allow_other option. You need allow_other if you want users other than the
# owner of a mounted fuse to access it. This option must appear on a line by
# itself. There is no value; just the presence of the option activates it.
#user_allow_other
# mount_max = n - this option sets the maximum number of mounts.
# Currently (2014) it must be typed exactly as shown
# (with a single space before and after the equals sign).
# It must be typed exactly as shown (with a single space before and after the
# equals sign).
#mount_max = 1000
+156 -33
View File
@@ -7,8 +7,8 @@
*/
/* This program does the mounting and unmounting of FUSE filesystems */
#define _GNU_SOURCE /* for clone */
#include "config.h"
#define _GNU_SOURCE /* for clone and strchrnul */
#include "fuse_config.h"
#include "mount_util.h"
#include <stdio.h>
@@ -24,7 +24,9 @@
#include <mntent.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include "fuse_mount_compat.h"
#include <sys/fsuid.h>
#include <sys/socket.h>
#include <sys/utsname.h>
@@ -36,26 +38,6 @@
#define FUSE_DEV "/dev/fuse"
#ifndef MS_DIRSYNC
#define MS_DIRSYNC 128
#endif
#ifndef MS_REC
#define MS_REC 16384
#endif
#ifndef MS_PRIVATE
#define MS_PRIVATE (1<<18)
#endif
#ifndef UMOUNT_DETACH
#define UMOUNT_DETACH 0x00000002 /* Just detach from the tree */
#endif
#ifndef UMOUNT_NOFOLLOW
#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
#endif
#ifndef UMOUNT_UNUSED
#define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
#endif
static const char *progname;
static int user_allow_other = 0;
@@ -63,6 +45,59 @@ static int mount_max = 1000;
static int auto_unmount = 0;
#ifdef GETMNTENT_NEEDS_UNESCAPING
// Older versions of musl libc don't unescape entries in /etc/mtab
// unescapes octal sequences like \040 in-place
// That's ok, because unescaping can not extend the length of the string.
static void unescape(char *buf) {
char *src = buf;
char *dest = buf;
while (1) {
char *next_src = strchrnul(src, '\\');
int offset = next_src - src;
memmove(dest, src, offset);
src = next_src;
dest += offset;
if(*src == '\0') {
*dest = *src;
return;
}
src++;
if('0' <= src[0] && src[0] < '2' &&
'0' <= src[1] && src[1] < '8' &&
'0' <= src[2] && src[2] < '8') {
*dest++ = (src[0] - '0') << 6
| (src[1] - '0') << 3
| (src[2] - '0') << 0;
src += 3;
} else if (src[0] == '\\') {
*dest++ = '\\';
src += 1;
} else {
*dest++ = '\\';
}
}
}
static struct mntent *GETMNTENT(FILE *stream)
{
struct mntent *entp = getmntent(stream);
if(entp != NULL) {
unescape(entp->mnt_fsname);
unescape(entp->mnt_dir);
unescape(entp->mnt_type);
unescape(entp->mnt_opts);
}
return entp;
}
#else
#define GETMNTENT getmntent
#endif // GETMNTENT_NEEDS_UNESCAPING
static const char *get_user_name(void)
{
struct passwd *pw = getpwuid(getuid());
@@ -169,7 +204,7 @@ static int may_unmount(const char *mnt, int quiet)
uidlen = sprintf(uidstr, "%u", getuid());
found = 0;
while ((entp = getmntent(fp)) != NULL) {
while ((entp = GETMNTENT(fp)) != NULL) {
if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
(strcmp(entp->mnt_type, "fuse") == 0 ||
strcmp(entp->mnt_type, "fuseblk") == 0 ||
@@ -261,7 +296,7 @@ static int check_is_mount_child(void *p)
}
count = 0;
while (getmntent(fp) != NULL)
while (GETMNTENT(fp) != NULL)
count++;
endmntent(fp);
@@ -280,7 +315,7 @@ static int check_is_mount_child(void *p)
}
found = 0;
while ((entp = getmntent(fp)) != NULL) {
while ((entp = GETMNTENT(fp)) != NULL) {
if (count > 0) {
count--;
continue;
@@ -464,7 +499,7 @@ static int count_fuse_fs(void)
strerror(errno));
return -1;
}
while ((entp = getmntent(fp)) != NULL) {
while ((entp = GETMNTENT(fp)) != NULL) {
if (strcmp(entp->mnt_type, "fuse") == 0 ||
strncmp(entp->mnt_type, "fuse.", 5) == 0)
count ++;
@@ -594,6 +629,14 @@ static struct mount_flags mount_flags[] = {
{"sync", MS_SYNCHRONOUS, 1, 1},
{"atime", MS_NOATIME, 0, 1},
{"noatime", MS_NOATIME, 1, 1},
{"diratime", MS_NODIRATIME, 0, 1},
{"nodiratime", MS_NODIRATIME, 1, 1},
{"lazytime", MS_LAZYTIME, 1, 1},
{"nolazytime", MS_LAZYTIME, 0, 1},
{"relatime", MS_RELATIME, 1, 1},
{"norelatime", MS_RELATIME, 0, 1},
{"strictatime", MS_STRICTATIME, 1, 1},
{"nostrictatime", MS_STRICTATIME, 0, 1},
{"dirsync", MS_DIRSYNC, 1, 1},
{NULL, 0, 0, 0}
};
@@ -991,7 +1034,7 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
* originally the same list as used by the ecryptfs mount helper
* (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
* but got expanded as we found more filesystems that needed to be
* overlayed. */
* overlaid. */
typeof(fs_buf.f_type) f_type_whitelist[] = {
0x61756673 /* AUFS_SUPER_MAGIC */,
0x00000187 /* AUTOFS_SUPER_MAGIC */,
@@ -1182,6 +1225,46 @@ static int send_fd(int sock_fd, int fd)
return 0;
}
/* Helper for should_auto_unmount
*
* fusermount typically has the s-bit set - initial open of `mnt` was as root
* and got EACCESS as 'allow_other' was not specified.
* Try opening `mnt` again with uid and guid of the calling process.
*/
static int recheck_ENOTCONN_as_owner(const char *mnt)
{
int pid = fork();
if(pid == -1) {
perror("fuse: recheck_ENOTCONN_as_owner can't fork");
_exit(EXIT_FAILURE);
} else if(pid == 0) {
uid_t uid = getuid();
gid_t gid = getgid();
if(setresgid(gid, gid, gid) == -1) {
perror("fuse: can't set resgid");
_exit(EXIT_FAILURE);
}
if(setresuid(uid, uid, uid) == -1) {
perror("fuse: can't set resuid");
_exit(EXIT_FAILURE);
}
int fd = open(mnt, O_RDONLY);
if(fd == -1 && errno == ENOTCONN)
_exit(EXIT_SUCCESS);
else
_exit(EXIT_FAILURE);
} else {
int status;
int res = waitpid(pid, &status, 0);
if (res == -1) {
perror("fuse: waiting for child failed");
_exit(EXIT_FAILURE);
}
return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
}
}
/* The parent fuse process has died: decide whether to auto_unmount.
*
* In the normal case (umount or fusermount -u), the filesystem
@@ -1217,10 +1300,21 @@ static int should_auto_unmount(const char *mnt, const char *type)
goto out;
fd = open(mnt, O_RDONLY);
if (fd != -1) {
close(fd);
} else {
result = errno == ENOTCONN;
switch(errno) {
case ENOTCONN:
result = 1;
break;
case EACCES:
result = recheck_ENOTCONN_as_owner(mnt);
break;
default:
result = 0;
break;
}
}
out:
free(copy);
@@ -1262,13 +1356,18 @@ int main(int argc, char *argv[])
int cfd;
const char *opts = "";
const char *type = NULL;
int setup_auto_unmount_only = 0;
static const struct option long_opts[] = {
{"unmount", no_argument, NULL, 'u'},
// Note: auto-unmount deliberately does not have a short version.
// It's meant for internal use by mount.c's setup_auto_unmount.
{"auto-unmount", no_argument, NULL, 'U'},
{"lazy", no_argument, NULL, 'z'},
{"quiet", no_argument, NULL, 'q'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"options", required_argument, NULL, 'o'},
{0, 0, 0, 0}};
progname = strdup(argc > 0 ? argv[0] : "fusermount");
@@ -1295,7 +1394,11 @@ int main(int argc, char *argv[])
case 'u':
unmount = 1;
break;
case 'U':
unmount = 1;
auto_unmount = 1;
setup_auto_unmount_only = 1;
break;
case 'z':
lazy = 1;
break;
@@ -1339,7 +1442,7 @@ int main(int argc, char *argv[])
exit(1);
umask(033);
if (unmount)
if (!setup_auto_unmount_only && unmount)
goto do_unmount;
commfd = getenv(FUSE_COMMFD_ENV);
@@ -1349,21 +1452,39 @@ int main(int argc, char *argv[])
goto err_out;
}
cfd = atoi(commfd);
{
struct stat statbuf;
fstat(cfd, &statbuf);
if(!S_ISSOCK(statbuf.st_mode)) {
fprintf(stderr,
"%s: file descriptor %i is not a socket, can't send fuse fd\n",
progname, cfd);
goto err_out;
}
}
if (setup_auto_unmount_only)
goto wait_for_auto_unmount;
fd = mount_fuse(mnt, opts, &type);
if (fd == -1)
goto err_out;
cfd = atoi(commfd);
res = send_fd(cfd, fd);
if (res == -1)
if (res != 0) {
umount2(mnt, MNT_DETACH); /* lazy umount */
goto err_out;
}
close(fd);
if (!auto_unmount) {
free(mnt);
free((void*) type);
return 0;
}
wait_for_auto_unmount:
/* Become a daemon and wait for the parent to exit or die.
ie For the control socket to get closed.
btw We don't want to use daemon() function here because
@@ -1412,10 +1533,12 @@ do_unmount:
goto err_out;
success_out:
free((void*) type);
free(mnt);
return 0;
err_out:
free((void*) type);
free(mnt);
exit(1);
}
+13 -11
View File
@@ -10,6 +10,7 @@ sysconfdir="$1"
bindir="$2"
udevrulesdir="$3"
useroot="$4"
initscriptdir="$5"
# Both sysconfdir and bindir are absolute paths (since they are joined
# with --prefix in meson.build), but need to be interpreted relative
@@ -36,18 +37,19 @@ if $useroot; then
fi
fi
install -D -m 644 "${MESON_SOURCE_ROOT}/util/udev.rules" \
if [ "${udevrulesdir}" != "" ]; then
install -D -m 644 "${MESON_SOURCE_ROOT}/util/udev.rules" \
"${DESTDIR}${udevrulesdir}/99-fuse3.rules"
install -D -m 755 "${MESON_SOURCE_ROOT}/util/init_script" \
"${DESTDIR}/etc/init.d/fuse3"
if test -x /usr/sbin/update-rc.d && test -z "${DESTDIR}"; then
/usr/sbin/update-rc.d fuse3 start 34 S . start 41 0 6 . || /bin/true
else
echo "== FURTHER ACTION REQUIRED =="
echo "Make sure that your init system will start the ${DESTDIR}/etc/init.d/fuse3 init script"
fi
if [ "$initscriptdir" != "" ]; then
install -D -m 755 "${MESON_SOURCE_ROOT}/util/init_script" \
"${DESTDIR}${initscriptdir}/fuse3"
if test -x /usr/sbin/update-rc.d && test -z "${DESTDIR}"; then
/usr/sbin/update-rc.d fuse3 start 34 S . start 41 0 6 . || /bin/true
else
echo "== FURTHER ACTION REQUIRED =="
echo "Make sure that your init system will start the ${DESTDIR}${initscriptdir}/init.d/fuse3 init script"
fi
fi
+11 -6
View File
@@ -11,19 +11,24 @@ executable('mount.fuse3', ['mount.fuse.c'],
link_with: [ libfuse ],
install: true,
install_dir: get_option('sbindir'),
c_args: '-DFUSE_USE_VERSION=35')
c_args: '-DFUSE_USE_VERSION=312')
udevrulesdir = get_option('udevrulesdir')
if udevrulesdir == ''
udev = dependency('udev')
udevrulesdir = join_paths(udev.get_pkgconfig_variable('udevdir'), 'rules.d')
udev = dependency('udev', required: false)
if udev.found()
udevrulesdir = join_paths(udev.get_variable(pkgconfig: 'udevdir'), 'rules.d')
endif
endif
if udevrulesdir == ''
warning('could not determine udevdir, udev.rules will not be installed')
endif
meson.add_install_script('install_helper.sh',
join_paths(get_option('prefix'), get_option('sysconfdir')),
join_paths(get_option('prefix'), get_option('bindir')),
udevrulesdir,
'@0@'.format(get_option('useroot')))
'@0@'.format(get_option('useroot')),
get_option('initscriptdir'))
+5 -1
View File
@@ -6,7 +6,7 @@
See the file COPYING.
*/
#include "config.h"
#include "fuse_config.h"
#include <stdio.h>
#include <stdlib.h>
@@ -43,6 +43,10 @@
#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED))
#endif
#endif
/* linux < 3.5 */
#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
#include "fuse.h"
+18
View File
@@ -0,0 +1,18 @@
To test FUSE with xfstests¹:
1. copy the `mount.fuse.passthrough` file into
`/sbin` and edit the `PASSTHROUGH_PATH`, `SCRATCH_SOURCE` and `TEST_SOURCE` variables as needed.
2. Make sure that the `SCRATCH_SOURCE` and `TEST_SOURCE` directories
exist.
3. Copy `local.config` into your xfstests directory
Tests can then be run with e.g.:
```sh
# make
# sudo ./check -fuse -b
```
¹https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git/about/
+12
View File
@@ -0,0 +1,12 @@
export TEST_DEV=non1
export TEST_DIR=/mnt/test
export SCRATCH_DEV=non2
export SCRATCH_MNT=/mnt/scratch
export FSTYP=fuse
export FUSE_SUBTYP=.passthrough
export MOUNT_OPTIONS=""
export TEST_FS_MOUNT_OPTS=""
PASSTHROUGH_PATH=/home/nikratio/libfuse/build/example/passthrough_hp
SCRATCH_SOURCE=/mnt/src/scratch
TEST_SOURCE=/mnt/src/test
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
ulimit -n 1048576
# It would be easier if we could just set SCRATCH_DEV and TEST_DEV to the source directory
# path in local.options. Unfortunately, setting these variables to a path seems get
# xfstests all worked up (even though it should treat these as opaque values), and it
# refuses to even start running any tests).
dev="$1"
shift
if [ "$dev" = "${SCRATCH_DEV}" ]; then
source="${SCRATCH_SOURCE}"
else
source="${TEST_SOURCE}"
fi
exec "$PASSTHROUGH_PATH" -o fsname=$dev,allow_other "${source}" "$@"