mirror of
https://github.com/openharmony/third_party_libfuse.git
synced 2026-07-19 13:34:16 -04:00
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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."
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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[])
|
||||
|
||||
@@ -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
@@ -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'
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
|
||||
#define FUSE_USE_VERSION 31
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#ifdef linux
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 *
|
||||
* ----------------------------------------------------------- */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <fuse_config.h>
|
||||
|
||||
#include <fuse.h>
|
||||
#include <stdio.h>
|
||||
|
||||
+75
-11
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
untrusted comment: signify public key
|
||||
RWRsNRJIAGSHE0C6WuVmusQlvudAPlmOOkCw1LbuOLuu+25DuAmPCpDf
|
||||
@@ -0,0 +1,2 @@
|
||||
untrusted comment: signify public key
|
||||
RWQtnc3WSoYwHGAdfvtTTVX8RsAXrNwMb8xqVwlY8lYY2Fxn2QUDiPYK
|
||||
Executable → Regular
+36
-11
@@ -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.
|
||||
Executable → Regular
+1
-2
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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',
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
|
||||
@@ -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/
|
||||
@@ -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
|
||||
@@ -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}" "$@"
|
||||
Reference in New Issue
Block a user